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

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

4

4 回答 4

43

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

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

所以父抽象 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);
    }
}

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() 方法中完成的。

我没有彻底测试过。但它与一个简单的 bean MyService 一起工作,该 bean 还从 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。

于 2014-02-20T16:37:05.533 回答
29

您正在尝试做的事情将使每个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现在,您的所有 servlet 都可以通过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>

然后覆盖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);
  }
[...]
}

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

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

依赖类被注释:

@Component
public class AnnotatedClassToInject

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

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

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

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

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

于 2013-09-11T15:49:49.790 回答