Java Spring注入到Servlet中

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18745770/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 10:40:22  来源:igfitidea点击:

Spring injection Into Servlet

javaspringservletsdependency-injection

提问by thatidiotguy

So I have seen this question:

所以我看到了这个问题:

Spring dependency injection to other instance

Spring 对其他实例的依赖注入

and was wondering if my method will work out.

并且想知道我的方法是否会奏效。

1) Declare beans in my Spring application context

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) Override my servlet's init method as shown:

2) 覆盖我的 servlet 的 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");
    }

Will this work or is Spring not yet ready to deliver beans to my servlet at this point in the web applications deployment? Do I have to do something more traditional like putting the beans in web.xml?

在 Web 应用程序部署的这一点上,这会起作用还是 Spring 尚未准备好将 bean 交付给我的 servlet?我是否必须做一些更传统的事情,比如把豆子放进去web.xml

采纳答案by Sotirios Delimanolis

What you are trying to do will make every Servlethave its own ApplicationContextinstance. Maybe this is what you want, but I doubt it. An ApplicationContextshould be unique to an application.

您正在尝试做的事情将使每个人Servlet都有自己的ApplicationContext实例。也许这就是你想要的,但我对此表示怀疑。AnApplicationContext应该是应用程序唯一的。

The appropriate way to do this is to setup your ApplicationContextin a ServletContextListener.

执行此操作的适当方法是ApplicationContextServletContextListener.

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

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

Now all your servlets have access to the same ApplicationContextthrough the ServletContextattributes.

现在您的所有 servlet 都可以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");
}

回答by sanbhat

Spring is independent of Servlet startup. Right after spring reads the bean xml it will be ready to deliver the beans. So right after below statement, beans are already available

Spring 独立于 Servlet 启动。在 spring 读取 bean xml 之后,它将准备交付 bean。所以在下面的声明之后,beans 已经可用

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

Also as pointed by @LuiggiMendoza each ApplicationContextwill create/maintain their own beans so its always good to create ApplicationContextonce and reuseit from different servlets (as opposed to creating them inside the init()method of a Servlet)

同样正如@LuiggiMendoza 所指出的,每个人ApplicationContext都会创建/维护自己的 bean,因此创建ApplicationContext一次并从不同的 servlet重用它总是好的(而不是在init()Servlet的方法中创建它们)

回答by Agustí Sánchez

I wanted to leverage on the solution provided by Sotirios Delimanolis but adding transparent autowiring to the mix. The idea is to turn plain servlets into autowire-aware objects.

我想利用 Sotirios Delimanolis 提供的解决方案,但在混合中添加透明的自动装配。这个想法是将普通 servlet 转换为自动装配感知对象。

So I created a parent abstract servlet class that retrieves the Spring context, gets and autowiring-capable factory and uses that factory to autowire the servlet instances (the subclasess, actually). I also store the factory as an instance variable in case the subclasses need it.

所以我创建了一个父抽象 servlet 类,它检索 Spring 上下文,获取和自动装配能力的工厂,并使用该工厂来自动装配 servlet 实例(实际上是子类)。我还将工厂存储为实例变量,以防子类需要它。

So the parent abstract servlet looks like this:

所以父抽象 servlet 看起来像这样:

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

And a sevlet subclass looks like 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());
    }
}

Notice the only thing EchoServlet needs to do is to declare a bean just in common Spring practice. The magic is done in the init() method of the superclass.

请注意,EchoServlet 唯一需要做的就是在常见的 Spring 实践中声明一个 bean。魔术是在超类的 init() 方法中完成的。

I haven't tested it thoroughly. But it worked with a simple bean MyService that also gets a property autowired from a Spring-managed properties file.

我还没有彻底测试过。但它与一个简单的 bean MyService 一起工作,该 bean 还从 Spring 管理的属性文件中获取自动装配的属性。

Enjoy!

享受!



Note:

笔记:

It's best to load the application context with Spring's own context listener like this:

最好像这样使用 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>

Then retrieve it like this:

然后像这样检索它:

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

Only spring-web library needs to be imported, not spring-mvc.

只需要导入spring-web库,不需要spring-mvc。

回答by Arigion

The answers here so far only worked partly for me. Especially classes with @Configuration annotation were ignored and I did not want to use an xml configuration file. Here is what I have done to get injection working working soley with an Spring (4.3.1) annotation based setup:

到目前为止,这里的答案只对我有用。特别是带有 @Configuration 注释的类被忽略了,我不想使用 xml 配置文件。这是我使用基于 Spring (4.3.1) 注释的设置使注入工作单独工作所做的工作:

Bootstrap an AnnotationConfigWebApplicationContext in web.xml under web-app. As parameter you need the contextClass and the contextConfigLocation (your annotated config class):

在 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>

Then overwrite the init method in the servlet. I use an abstract class which extends HttpServlet, so I don't have to repeat it in every servlet:

然后覆盖 servlet 中的 init 方法。我使用了一个扩展 HttpServlet 的抽象类,所以我不必在每个 servlet 中重复它:

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

and finally I have my main configuration in the AppConfig class mentioned in web.xml:

最后我在 web.xml 中提到的 AppConfig 类中有我的主要配置:

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

The dependend classes are annotated:

依赖类被注释:

@Component
public class AnnotatedClassToInject

and injected via autowiring in my servlet:

并通过自动装配注入我的 servlet:

@Autowired
private AnnotatedClassToInject myClass;