Подтвердить что ты не робот

Spring инъекция в сервлет

Итак, я видел этот вопрос:

Spring инъекция зависимостей в другой экземпляр

и задавался вопросом, будет ли мой метод работать.

1) Объявите beans в моем контексте Spring

    <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) Переопределите мой метод инициализации сервлета, как показано:

    @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 еще не готова доставить beans на мой сервлет на этом этапе развертывания веб-приложений? Должен ли я сделать что-то более традиционное, например, поставить beans в web.xml?

4b9b3361

Ответ 1

Что вы пытаетесь сделать, каждый 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");
}

Ответ 2

Я хотел использовать средства, предоставленные Sotirios Delimanolis, но добавляя прозрачное автоувеличивание в микс. Идея состоит в том, чтобы превратить простые сервлеты в объекты, поддерживающие autwire.

Итак, я создал родительский абстрактный класс сервлета, который извлекает контекст Spring, получает и поддерживает autwiring factory и использует этот factory для автоматического создания экземпляров сервлета (подклассы, на самом деле). Я также сохраняю factory как переменную экземпляра в случае необходимости в подклассах.

Итак, родительский абстрактный сервлет выглядит следующим образом:

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 объявить bean обычную практику Spring. Магия выполняется в методе init() суперкласса.

Я не тестировал его полностью. Но он работал с простым bean MyService, который также получает свойство, автоматически открытое из файла свойств Spring.

Наслаждайтесь!

+++++++++++++++

Примечание:

Лучше всего загрузить контекст приложения с помощью 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 -web, а не Spring -mvc.

Ответ 3

Ответы здесь пока отчасти отчасти для меня. Особенно классы с аннотацией @Configuration были проигнорированы, и я не хотел использовать файл конфигурации xml. Вот что я сделал, чтобы заставить работать инъекции работать с помощью установки Spring (4.3.1) на основе аннотации:

Загрузите аннотациюConfigWebApplicationContext в web.xml в веб-приложении. В качестве параметра вам нужен контекстный кластер и 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);
  }
[...]
}

и, наконец, у меня есть моя основная конфигурация в классе AppConfig, упомянутом в web.xml:

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

Зависимые классы аннотируются:

@Component
public class AnnotatedClassToInject

и вводится с помощью autowiring в моем сервлете:

@Autowired
private AnnotatedClassToInject myClass;

Ответ 4

Spring не зависит от запуска сервлета. Сразу после spring читает bean xml, он будет готов к доставке beans. Итак, сразу после утверждения beans уже доступны

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

Также, как указано @LuiggiMendoza, каждый ApplicationContext создаст/сохранит свой собственный beans, поэтому всегда полезно создать ApplicationContext один раз и повторно использовать его из разных сервлетов (в отличие от создания их внутри метода init() сервлета)