• 监听器中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容器中对象
     
     
     
     
     
  • 相关阅读:
    10 个非常有用的 AngularJS 框架
    15个最新加速 Web 开发的框架和工具
    最新13个加速 Web 开发的框架和工具
    7款个性化jQuery/HTML5地图插件
    19款绚丽实用的jQuery/CSS3侧边栏菜单
    7 个顶级的 HTML5 Canvas 动画赏析
    10款精美的web前端源码的特效
    10款web前端基于html5/CSS3应用特效
    8个强大的基于Bootstrap的CSS框架
    7款经典炫酷的HTML5/jQuery动画应用示例及源码
  • 原文地址:https://www.cnblogs.com/leskang/p/6410926.html
Copyright © 2020-2023  润新知