1. 程式人生 > 其它 >springmvc專案中InitializingBean執行2次

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>