在写一个文件暂存系统的时候,有这样的一个需求:一个定时任务,每隔t分钟,做一些扫描操作。
实现思路是继承ContextLoaderListener,实现自己的Listener,在这个Listener中,开启定时任务。
一开始直接使用@Componet注解+@Autowired注解,结果导致注入失败。
由于一开始没意识到是线程的原因,所以在上网搜的原因及解决方案都是无效的,但也有一些有用的知识:
1.一般@Autowired注入失败有以下一个原因:
1.包没扫描到。
2.controller层的@controller或者service层的@Service注解没写
3.如果使用@Resource 注入service需要注意对象名字。
2.默认情况下,<context:component-scan>查找使用构造型(stereotype)注解所标注的类,如@Component(组件),@Service(服务),@Controller(控制器),@Repository(数据仓库)。
3.Spring和SpringMVC整合的问题,二者在整合的时候,会产生两个容器,一个Spring容器,一个SpringMVC容器。Spring容器是父容器,SpringMVC容器是Spring容器的子容器。Spring容器初始化后,SpringMVC再进行初始化,并将Spring容器作为它的父容器。
子容器能够访问父容器的bean,而反之不行。
当父容器的初始化路径(base-package)等于或者包括了子容器的路径,那么可能出现,当SpringMVC子容器初始化时,如果发现有相同的类,会将新初始化的实例覆盖Spring容器中已经存在的实例,这可能导致注入失败,空指针的问题。
所以要做到2个容器要扫描的包必须相互区分,互不干扰。Spring容器扫描非Controller的实例,而SpringMVC容器扫描Controller的实例就行。
然后在一个论坛中得知了是线程的原因:
“因为主线程中可以直接使用@Autowired注解进行注入,但是在新开的线程中就不可以进行注入,每次启动服务器就会报空指针异常,新开启的线程直接就挂掉了,网上也搜了好多,原来是spring不会去管理这个新的线程,所以也就注入不进去任何bean。注解得方式最终都不能注入。”
有以下几种方式来获取,我使用的是:通过工具类获取。Spring工具类获取bean。
这种方法必须要把该工具类在SpringApplicationContext中注册bean,这样Spring才能通过工具类的set方法给其注入context。这样也带出了这种方法的弊端:工具类必须在Spring容器初始化完之后才能工作。
在现有的实现思路的基础上,这个弊端立刻就显现出来了,在自己重写的WebContextListener运行的时候,Spring容器是没有初始化的!所以会报空指针异常。
解决思路:工具类必须在Spring容器初始化完后才可以正常工作,那么可以找一个合适的位置开启(仅开启一次)任务调度器。