25

だから私はこの質問を見ました:

他のインスタンスへの Spring 依存性注入

私の方法がうまくいくかどうか疑問に思っていました。

1) Spring アプリケーションコンテキストで Bean を宣言する

    <bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <property name="initialSize" value="${jdbc.initialSize}" />
        <property name="validationQuery" value="${jdbc.validationQuery}" /> 
        <property name="testOnBorrow" value="${jdbc.testOnBorrow}" />
    </bean>

    <bean id="apiData" class="com.mydomain.api.data.ApiData">
        <property name="dataSource" ref="dataSource" />
        <property name="apiLogger" ref="apiLogger" />
    </bean>

    <bean id="apiLogging" class="com.mydomain.api.data.ApiLogger">
        <property name="dataSource" ref="dataSource" />
    </bean>

2) サーブレットの init メソッドを次のようにオーバーライドします。

    @Override
    public void init(ServletConfig config) throws ServletException {
       super.init(config);

       ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

       this.apiData = (ApiData)ac.getBean("apiData");
       this.apiLogger = (ApiLogger)ac.getBean("apiLogger");
    }

これは機能しますか、それとも Web アプリケーションのデプロイのこの時点で、Spring はまだ Bean をサーブレットに配信する準備ができていませんか? 豆を入れるなど、もっと伝統的なことをしなければなりませんweb.xmlか?

4

4 に答える 4

43

Sotirios Delimanolis によって提供されたソリューションを活用したかったのですが、透過的な自動配線をミックスに追加しました。アイデアは、単純なサーブレットをオートワイヤー対応のオブジェクトに変えることです。

そこで、Spring コンテキストを取得し、オートワイヤリング対応のファクトリを取得し、そのファクトリを使用してサーブレット インスタンス (実際にはサブクラス) をオートワイヤする親抽象サーブレット クラスを作成しました。また、サブクラスが必要とする場合に備えて、ファクトリをインスタンス変数として保存します。

したがって、親抽象サーブレットは次のようになります。

public abstract class AbstractServlet extends HttpServlet {

    protected AutowireCapableBeanFactory ctx;

    @Override
    public void init() throws ServletException {
        super.init();
        ctx = ((ApplicationContext) getServletContext().getAttribute(
                "applicationContext")).getAutowireCapableBeanFactory();
        //The following line does the magic
        ctx.autowireBean(this);
    }
}

サーブレットのサブクラスは次のようになります。

public class EchoServlet extends AbstractServlet {

    @Autowired
    private MyService service;

    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws IOException, ServletException {
        response.getWriter().println("Hello! "+ service.getMyParam());
    }
}

EchoServlet が行う必要がある唯一のことは、一般的な Spring の慣行で Bean を宣言することだけであることに注意してください。魔法は、スーパークラスの init() メソッドで行われます。

私はそれを徹底的にテストしていません。ただし、Spring 管理のプロパティ ファイルから自動配線されたプロパティも取得する単純な Bean MyService で動作しました。

楽しみ!


ノート:

次のように、Spring 独自のコンテキスト リスナーを使用してアプリケーション コンテキストをロードすることをお勧めします。

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

次に、次のように取得します。

WebApplicationContext context = WebApplicationContextUtils
    .getWebApplicationContext(getServletContext());
ctx = context.getAutowireCapableBeanFactory();
ctx.autowireBean(this);

spring-mvc ではなく、spring-web ライブラリのみをインポートする必要があります。

于 2014-02-20T16:37:05.533 に答える
29

あなたがやろうとしていることは、すべてServletに独自のApplicationContextインスタンスを持たせることです。多分これはあなたが望んでいることですが、私はそれを疑っています。ApplicationContextはアプリケーションに固有である必要があります。

これを行う適切な方法は、 でセットアップするApplicationContextことServletContextListenerです。

public class SpringApplicationContextListener implements ServletContextListener {
        @Override
    public void contextInitialized(ServletContextEvent sce) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

        sce.getServletContext().setAttribute("applicationContext", ac);            
    }
    ... // contextDestroyed
}

これで、すべてのサーブレットが属性ApplicationContextを介して同じものにアクセスできるようになりました。ServletContext

@Override
public void init(ServletConfig config) throws ServletException {
   super.init(config);

   ApplicationContext ac = (ApplicationContext) config.getServletContext().getAttribute("applicationContext");

   this.apiData = (ApiData)ac.getBean("apiData");
   this.apiLogger = (ApiLogger)ac.getBean("apiLogger");
}
于 2013-09-11T15:58:35.040 に答える
8

これまでのところ、ここでの答えは部分的にしか機能しませんでした。特に @Configuration アノテーションを持つクラスは無視され、xml 構成ファイルを使用したくありませんでした。これは、Spring (4.3.1) アノテーションベースのセットアップでのみインジェクションを機能させるために行ったことです。

web-app の下の web.xml で AnnotationConfigWebApplicationContext をブートストラップします。パラメータとして、contextClass と contextConfigLocation (注釈付きの構成クラス) が必要です。

<context-param>
    <param-name>contextClass</param-name>
    <param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
  </param-value>
</context-param>
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>com.example.config.AppConfig</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

次に、サーブレットの init メソッドを上書きします。HttpServlet を拡張する抽象クラスを使用するため、すべてのサーブレットで繰り返す必要はありません。

@Configurable
public abstract class MySpringEnabledServlet extends HttpServlet
{
  @Override
  public void init(
      ServletConfig config) throws ServletException
  {
    super.init(config);
    SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
  }
[...]
}

そして最後に、web.xml で言及されている AppConfig クラスにメインの構成があります。

@Configuration
@ComponentScan(basePackages = "com.example")
@Import(
{ SomeOtherConfig.class })
public class AppConfig
{
}

依存クラスには注釈が付けられます。

@Component
public class AnnotatedClassToInject

私のサーブレットに自動配線を介して注入されます:

@Autowired
private AnnotatedClassToInject myClass;
于 2016-07-06T07:14:41.953 に答える
1

Spring はサーブレットの起動に依存しません。Spring が Bean xml を読み取った直後に、Bean を配信する準備が整います。したがって、以下のステートメントの直後に、Bean は既に利用可能です

ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

また、@LuiggiMendoza が指摘しているように、それぞれApplicationContextが独自の Bean を作成/維持するため、 (サーブレットのメソッド内で作成するのではなく)ApplicationContext 一度作成して別のサーブレットから再利用することは常に良いことです。init()

于 2013-09-11T15:49:49.790 に答える