web-dev-qa-db-ja.com

サーブレットへの春の注入

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

他のインスタンスへの依存関係の注入

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

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");
    }

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

23
thatidiotguy

あなたがしようとしていることは、すべての Servlet が独自の ApplicationContext インスタンスを持つようにします。たぶんこれはあなたが望むものですが、私はそれを疑います。 ApplicationContextはアプリケーションに固有でなければなりません。

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

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");
}
28

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);
    }
}

また、sevletサブクラスは次のようになります。

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ライブラリのみをインポートする必要があります。

38

ここまでの答えは、これまでのところ私には部分的にしか機能しませんでした。特に@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;
8
Arigion

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

_ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
_

また、@ LuiggiMendozaが指摘しているように、各ApplicationContextは独自のBeanを作成/維持するので、ApplicationContextを作成することは常に適切です一度だけ再利用異なるサーブレットからサーブレットのinit()メソッド内で作成)

1
sanbhat