监听器中spring注入相关的问题

问题描述:
需求是要求在项目启动自动触发一个service中的线程的操作,使用监听器来实现,但是自定义监听器中spring注解service失败,通过WebApplicationContextUtils去spring容器中获取仍然获取不到,通过断点查看spring容器中没有被注入的service对象
 
代码如下:
1、web.xml文件中配置监听器
<listener>
    <listener-class>com.cairh.xpe.aips.web.Test.TestLinstener</listener-class>
</listener>

 2、写监听器实现ServletContextListener并重写相应的方法

public class TestLinstener implements ServletContextListener{
    
    @Autowired
    MessMediaService messMediaService;
    
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("*************执行相关方法************");
        //spring容器中获取service对象
        MessMediaService service = WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext()).getBean(MessMediaService.class);
        service.test();    
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        // TODO Auto-generated method stub
    }

}

3、service类

@Lazy(false)
@Service
public class MessMediaService {
    
    @Autowired
    private ITaskService taskService;
    
    private RedisLock redisLock = new RedisLock();
    
    private Map taskMap = (Map)JSON.parse(RedisClientUtil.get("config.cache./aips/tasksettings.properties"));
    
   private Thread thread = new Thread(new Runnable() {
        
        @Override
        public void run() {

        }
    });
    
    public int test(){
    taskService.test();
     ...
        return 1;
    }
    
    public MessMediaService() {
        super();
        System.out.println("*******************测试构造方法********************");
    }
    
    @PostConstruct
    private void init() throws Exception {
        System.out.println("*******************init******************");
        thread.start();
    }
    
}

 解决历程:

后来尝试在构造方法中触发事件也失败,因为断点并没有进入到构造方法中;
再后来尝试使用@PostConstruct注解让service在实例化过程中自动执行方法来实现,@PostConstruct执行顺序大致如下:
服务器加载servlet-->servlet构造函数-->@PostConstruct方法-->init-->service方法-->destroy方法-->@PreDestory方法-->服务器卸载servlet完毕
 
通过此方法正常来说也可实现项目启动自动触发一定的操作,但是启动时并没有按照预想进入到注解的方法中执行任何操作
注:@PostConstruct详细解释见:@PostConstruct与@PreDestroy讲解及实例
 
分析:
构造方法没有执行并且spring容器中没有messMediaService实例,说明要么没有实例化要么是实例化失败
首先验证是否实例化失败,在普通的controller中注入此service,在调用controller时成功,说明service本身没有错误,不会导致实例化失败,剩下的可能就是在项目启动加载的时候没有实例化service
 
在查看spring配置文件的时候发现了beans属性default-lazy-init="true",发现问题所在,此属性表示延时加载,为了提高平时开发中项目启动时间设置的,就是在IoC容器启动时不会实例化bean,只有当容器需要用到时才实例化它,故在监听器中并没有实例化service
 
解决:
如果是手动配置则可在bean属性中添加lazy-init="false"属性来对需要提前加载的bean在spring容器启动时实例化,或者使用注解标签@Lazy(false)
 
再次尝试:
1、监听器中注入messMediaService依然失败;
2、spring容器中获取messMediaService对象成功,可通过从spring容器中获取messMediaService对象来调用,可满足需求;
3、在messMediaService构造方法中断点发现执行到service构造方法时service中注入的taskService依然为null;
4、在@PostConstruct注解方法中发现messMediaService中注入的taskService也有了实例,此方法可成功解决问题;
 
总结:
项目启动最先加载context-parame标签中配置的spring文件,此文件中配置了spring需要实例化的bean目录,但是spring注入和bean的实例化不是同时的,先实例化再注入,两步操作是紧接着的,但是在实例化调用构造方法完成后才有依赖注入行为。
注:spring实例化详细解释见:spring容器初始化过程 和 Spring BeanFactory实例化Bean的详细过程
 
项目启动执行顺序:调用构造方法实例化-->调用@PostConstruct方法-->执行listener的contextInitialized方法
 
listener中messMediaService没有注入成功而messMediaService中能注入taskService是因为messMediaService实例化时其属性service属于一种依赖关系也会被实例化并注入进来,而listener不会被spring实例化并查找依赖关系,故监听器中不能使用spring注解,只能手动获取spring容器中对象
 
 
 
 
 
原文地址:https://www.cnblogs.com/leskang/p/6410926.html