springmvc项目中InitializingBean执行2次
原创
©著作权归作者所有:来自51CTO博客作者buguge的原创作品,请联系作者获取转载授权,否则将追究法律责任
为了修复生产数据,需要执行一段一次性的代码。 鉴于是spring老项目,就想到了InitializingBean。
代码如下。服务启动后,log里发现出现2条“一次性任务开始”。 好在里面逻辑做了防重控制,没有受到什么影响。
@Slf4j
@Component
public class TransToBankBean implements InitializingBean {
@Autowired
private FixedLdysZhOrdersService xxxService;
@Override
public synchronized void afterPropertiesSet() {
log.info("一次性任务开始");
....
}
}
今天理了一下程序配置。发现web.xml配置有问题。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml,classpath:spring-mybatis.xml,classpath:spring-dubbo.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
</listener>
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>/index.jsp</welcome-file>
</welcome-file-list>
</web-app>
注意到上面web.xml中有两个contextConfigLocation, 一个位于context-param参数中, 另一个位于servelt的init-param参数中。
问题就出现在这个contextConfigLocation上。
contextConfigLocation,名如其义,指的是context配置(文件)的位置。
servelt/init-param的contextConfig是为了加载DispatcherServlet的, 而context-param的contextConfig是为了加载web程序需要加载的数据库等等配置。
再来说说servlet节点配置:
servlet有这么几个属性:servlet-name、servlet-class、init-param。其中,servlet-class通常就是我们熟知的 DispatcherServlet。init-param中可以指定contextConfigLocation。
1) init-param里如果未配置contextConfigLocation,则要求程序在WEB-INF存在名为[servlet-name]-servlet.xml的配置文件,否则程序启动会报异常:java.io.FileNotFoundException:Could not open ServletContext resource [/WEB-INF/SpringMVC-servlet.xml] 。(注意:我这里servlet-name的值是SpringMVC,所以文件名字会是 SpringMVC-servlet.xml)
2) init-param里如果有contextConfigLocation配置,则DispatcherServlet会使用这个指定的配置文件作为配置。例如,我指定的参数值是classpath:spring-mvc.xml, 这个文件定义在main/resources下,编译后存在于程序包的classes目录中。
显然,上面web.xml中两个contextConfigLocation都指定了spring-mvc.xml。这个context文件里指定了component-scan包扫描路径。
上面定义的InitializingBean实现类就在这些package下面。所以,不难理解,这个类所覆写的afterPropertiesSet会被执行两次。
好,了解了上面的解释。那么,我们就知道该怎么改了。------>分离配置,解决扫描两遍的问题。
改造后的web.xml如下, 两处contextConfigLocation分别指定的是application-context.xml 和 spring-mvc.xml。两者各司其职 -----> application-context.xml是spring应用程序的上下文配置,不含springmvc配置; spring-mvc.xml中只有springmvc配置。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
</listener>
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>/index.jsp</welcome-file>
</welcome-file-list>
</web-app>
spring-mvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扩充了注解驱动,可以将请求参数绑定到控制器参数 -->
<mvc:annotation-driven/>
<mvc:default-servlet-handler/>
<!-- controller所在包-->
<context:component-scan base-package="com.levy.rpcprovider.controller"/>
</beans>