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
?
Ответы
Ответ 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()
сервлета)