buguge - Keep it simple,stupid

知识就是力量,但更重要的,是运用知识的能力why buguge?

导航

springmvc项目中InitializingBean执行2次

为了修复生产数据,需要执行一段一次性的代码。 鉴于是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>

 

posted on 2022-05-18 22:19  buguge  阅读(455)  评论(0编辑  收藏  举报