listener中获取spring中管理的bean

看第一个例子:

自定义一个listener:

package com.test.common.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.springframework.beans.factory.annotation.Autowired;

import com.test.moudules.test.service.MyService;

public class CustomListener implements ServletContextListener {
    
    @Autowired
    MyService myService;

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
    }

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("*****started*******");
        myService.test();
    }

}

在web.xml中进行配置:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>testWeb</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>
  
   <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-context*.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
     <listener>
        <listener-class>com.test.common.listener.CustomListener</listener-class>
    </listener>
<!-- springmvc 前端控制器 -->
    <servlet>
        <servlet-name>dispatcherSerlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherSerlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

MyService(纳入spring管理的普通bean):

package com.test.moudules.test.service;

import org.springframework.stereotype.Component;

@Component
public class MyService {

    public void test() {
        System.out.println("this is test method");
    }
}

启动web应用结果:

java.lang.NullPointerException
    at com.test.common.listener.CustomListener.contextInitialized(CustomListener.java:22)

分析原因:

listener(以及filter)是由servlet容器(比如tomcat)来进行管理的,而像MyService这些bean是由spring容器管理的,所以在CustomListener中是识别不了自动注入的注解(@Autowired),也无法获取到spring容器中的MyService这些bean的,所以报空指针异常。

看第二个例子:

package com.test.common.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.springframework.web.context.support.WebApplicationContextUtils;

import com.test.moudules.test.service.MyService;

public class CustomListener implements ServletContextListener {

    MyService myService;

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
    }

//servlet容器启动时执行的方法
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("*****started*******");
        myService = WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext())
                .getBean(MyService.class);
        myService.test();
    }

}

启动web应用结果:

*****started*******
this is test method

分析原因:看结果是执行成功了拿到了MyService的bean,并执行了方法。如何实现呢?我们最终的目标是要拿到spring管理的bean(像MyServlce这些类的实现),通过

WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext())可以拿到spring容器的applicationContext,进而拿到相关的bean。

注意:

配置顺序必须如下:

 <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-context*.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
     <listener>
        <listener-class>com.test.common.listener.CustomListener</listener-class>
    </listener>

即让spring容器先启动起来,再启动自定义的listener,若顺序反了肯定也拿不到相应的bean,因为spring容器还未启动。

 看第三个例子:

package com.test.common.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;

import com.test.moudules.test.service.MyService;

public class AutoWiredBeanListener implements ServletContextListener {

    @Autowired
    MyService myService;

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
    }

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        // TODO Auto-generated method stub
        SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, sce.getServletContext());
        System.out.println("SpringBeanAutowiringSupport");
        myService.test();

    }

}
SpringBeanAutowiringSupport
this is test method

看执行结果,通过使用@Autowired注解的方式也可以注入spring管理的bean,此处是使用了

SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, sce.getServletContext());传入当前对象和servletContext。方法的解释为:

Process {@code @Autowired} injection for the given target object, based on the current root web application context as stored in the ServletContext.(根据传入的servletContext所关联的webApplicationContext,处理相关的注入,具体原理可跟进代码分析,依赖于AutowiredAnnotationBeanPostProcessor)。

原文地址:https://www.cnblogs.com/silenceshining/p/12466354.html