使用idea编译器中的Tomcat时写在逻辑业务层的定时器会执行两次,最开始我认为是Tomcat的问题,后面做了个测试在出现该现象的业务层类中定义了一个测试定时器方法:
@Scheduled(cron ="0/5 * * * * ?") //每隔5秒执行一次这个方法
public void test(){
System.out.println(this);
}
控制台输出:
可以看到该对象会输出两个不同地址,我意识到了该类被实例化了两次 ,在网上查找了相关信息,总结了出现该问题的原因:
原因1.使用了Tomcat实例中的配置的应用程序,而其中的配置文件中的server.xml文件中的
<Host name="localhost" appBase="webapp"
unpackWARs="true" autoDeploy="false">
<Context path="/user/header" docBase="F:/uploadfile/user/header"/>
appBase为webapp的同时下面的docBase不为空,为了查到原因,我特意去看了下HOST标签与Context标签代表的作用:
<Host>元素
作用:表示一个虚拟主机,为特定的虚拟主机处理所有请求
<context>元素
作用:一个WEB应用程序,处理当前WEB应用程序的所有请求,每一个<Context>必须使用唯一的上下文路径。
如果docBase指定的WAR包或应用目录就在docBase中,则不需要指定,因为Tomcat会自动扫描appBase中的WAR包和应用目录,指定了反而会造成问题。
这也是有人会出现问题的所在,我看了下出现这个
问题的小伙伴的配置docBase路径多是相对路径,Tomcat在执行时会出现扫描两次的时候,所以出现了两次或多次的初始化定时器
关于server.xml详解
原因2:没有使用了Tomcat实例中的配置的应用程序,会自动部署场景,配置文件多会自动部署,而自动部署的server.xml文件中的appBase值为webapps,没有docBase,但也会出现两次
初始化定时器,有人已经知道是容器的问题有的人,网上有的解释的是ContextLoaderListener与DispatcherServlet会创建出两个容器,这样会导致初始化两个bean,于是他们就删掉其中的一个,
其中的原理是什么呢?,我们先来简单看看ContextLoaderListener与DispatcherServlet两者的关系与区别:
ContextLoaderListener是上下文加载监听器用于加载应用中所有的bean,是父容器,
DispatcherServlet是前端控制器是用于对前端请求关系与对应的Controler层的bean对象的映射关系
他俩的关系:
ContextLoaderListener 创建根应用程序上下文
DispatcherServlet 条目为每个servlet条目创建一个子应用程序上下文。
子上下文可以访问根上下文中定义的bean。
根上下文中的Bean无法直接访问子上下文中的bean。
所有上下文都被添加到ServletContext。
你可以使用WebApplicationContextUtils类访问根上下文。
通过getbean获取bean时先回从子容器中查找,如果子容器没有就会去父容器,而查找的过程中会扫描相对应的配置文件,里面的扫描器多会开启扫描,将扫描到的bean转载到容器中,而SSM整合的过程中我们习惯将Spring与Springmvc配置文件合并为application.xml,正是因为这样在对,通过web.xml:
application.xml:
会先在子容器中找,此时目的是为了去进行子容器的bean装箱,也就是去执行
<context:component-scan base-package="Controller"></context:component-scan>
也就是因为两个配置文件合并了在对子容器的bean装箱的时候开启了父容器的bean的装箱
这是因为定时器写在了除Control层的bean上,如持久层、逻辑业务层才会出现的错误,
而写在控制层却不会出现问题的原因了,现在大家知道出现这种情况的原因了吧。而上面讲的网友的解决方法是不全对的,如果你删掉的是DispatcherServlet,就会报一下错:
如果你删掉 ContextLoaderListener不会报错,定时器也只执行了一次,那么就是说明ContextLoaderListener没用咯?其实不然,因为在对子容器进行初始化的时候会对父容器进行初始化,在tomcat启动过程中,创建了DispatcherServlet
,该servlet继承FrameworkServlet:
这里有个initServletBean方法,该方法会在tomcat启动时被调用!从而创建spring容器(WebApplicationContext),删除ContextLoaderListener和将两个配置文件分开写多测试成功:
大功告成!!!
所以说其实两个容器多是应用程序的必须,以上就是 使用idea编译器中的Tomcat时写在逻辑业务层的定时器会执行两次的原因。
如果对你有用,那就:
本文参考的资料有:
https://blog.csdn.net/qq_37063860/article/details/97275997
https://blog.51cto.com/u_15064650/3769909?b=totalstatistic
web.xml只需配置DispatcherServlet无需配置ContextLoaderListener的原因_Rico_Yip的博客-CSDN博客