項目就啟動了兩次,程序倒是正常運行,關鍵我里邊寫了個while 循環,不能讓它啟動兩次啊
百度了一下,有人說是tomcat server.xml或者tomcat新建服務的時候設置出了問題 ....最終發現不是這里問題,下文有最終問題所在,解決問題過程是我一步步理了理tomcat的啟動,加載配置文件.可謂是一步不理解 的系統,無法解決問題
存在上邊的問題, tomcat 是一方面,web.xml配置是另一個問題所在, 如果是按照默認的那種方式加上的tomcat server ,tomcat的問題就不用找了.如果是修改了項目自動部署到tomcat這塊那就可能是tomcat的問題了
這里有tomcat server.xml 一篇文章詳細介紹了 其中的參數說明,仔細看看,相信會排查出是否跟tomcat有關了https://www.cnblogs.com/kismetv/p/7228274.html#title1 寫的很好.
總結一下就是,默認 eclipse 加載了tomcat 並沒有使用tomcat 安裝目錄的webapps,打開項目里邊servers 下的server.xml .
這個文件是tomcat 當前項目用到環境用到的server.xml,這個文件是從安裝目錄的server.xml 加過來的,右鍵屬性可以看到當前所在的目錄
這個目錄是在哪訂的呢看下圖 ,雙擊server欄目下當前的server.紅框內就是當前項目制定的server目錄 配置文件path.
configuration path 就是這個目錄
默認選第一個沒問題 ,第二個為tomcat 的安裝目錄這個配置是將項目發布到tomcat的webapps下。
在servers試圖啟動Tomcat后,調用的是tomcat所在目錄的執行文件,除了部署eclipse下的項目,tomcat還要加載webapps下的所有項目,所以就重復加載了。
那是配置文件里邊host 里邊的配置出現問題
錯誤配置:
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true" xmlValidation="false" xmlNamespaceAware="false"> <Context docBase="/usr/local/apache-tomcat-6.0.29/webapps/XXX" path="" reloadable="true"></Context> <Context docBase="/usr/local/apache-tomcat-6.0.29/webapps/XXX" path="/admin" reloadable="true"></Context> </Host>
以上配置,由於host節點配置了appBase為webapps,所有tomcat會加載webapps里的所有項目,下面又配置了webapps里的項目,導致項目又加載一次,所以會導致項目重復加載,定時程序會在幾秒之內重復執行,后來改了一下配置好了,
如下正確配置:
<Host name="localhost" appBase="" unpackWARs="true" autoDeploy="true" xmlValidation="false" xmlNamespaceAware="false"> <Context docBase="/usr/local/apache-tomcat-6.0.29/webapps/XXX" path="" reloadable="true"></Context> <Context docBase="/usr/local/apache-tomcat-6.0.29/webapps/XXX" path="/admin" reloadable="true"></Context> </Host>
<Host name="localhost" appBase="webapps" 這個appBase 默認是找tomcat 安裝目錄下的webapps ,因為eclipse 改了個項目部署路徑為下圖:
部署路徑為上圖中tmp2 不同項目可能是tmp1 ,下的wtpwebapps 下邊,war文件解壓后的項目路徑相同的文件.
接上既然eclipse已經默認設置了這個路徑.,那tomcat server.xml 里邊的host 內的webapps 在此是不作數的. 上文有個server.xml介紹的文章里已經說明,如果部署路徑不在webapps內,要在
<Context docBase= 內指定,那我們看看部署后eclipse 自動加了一行context,這是原先tomcat下的server.xml 沒有的,於是乎 啟動項目就能正常加載我們自己的項目了...所以只要不是自己指定的部署路徑,默認的eclipse的server.xml不存在問題.當然還有一些配置域名的需求的可以參考上邊
出現的問題.
<Context docBase="AppService" path="/AppService" reloadable="true" source="org.eclipse.jst.jee.server:AppService"/></Host>
以上就是會出現百度上別人所說 的配置問題導致的兩次啟動.而這種聲音壓過了spring 和springmvc 兩個配置文件中的問題.
看web.xml 文件里的一個配置
<servlet> <servlet-name>applicationContext</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-common.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> <async-supported>true</async-supported> </servlet> <servlet-mapping> <servlet-name>applicationContext</servlet-name> <url-pattern>/</url-pattern> <url-pattern>*.json</url-pattern> </servlet-mapping> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-common.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class> </listener> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
<init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-common.xml</param-value> </init-param>
與
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-common.xml</param-value> </context-param>
內 <param-value>classpath:spring-common.xml</param-value> 相同
結果就這么悲劇了
<context-param> 是spring 加載的配置文件
<init-param> 是springmvc 加載的配置文件 .這兩者是分開加載的,起先是混為一談了. 別人的配置的運行的正常也沒在意
通過把上邊的<init-param> 注釋掉,程序確實加載了一次,提示找不到...xml配置文件.所以不能注釋掉.
把 <param-value> 和<context-param> 的配置文件分開吧,總之都需要配置,另一個spring 的建一個空文件掛上去.問題解決.后期如果有配置項也 可以放在spring指定的配置文件里.
正確的配置
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> <!-- <param-value>classpath:spring-common.xml</param-value> --> </context-param>
於是聲勢浩盪的項目啟動兩次的問題最終解決.....
等等...問題還沒解決...這里還有spring 和springmvc兩個容器加載bean 的問題, springmvc是spring 的子容器,加了控制器,如下圖
所以spring的配置文件不能為空.....如果所有配置都在一個文件里, 那肯定是不行了,
一、Spring和SpringMVC的父子容器關系
1.講問題之前要先明白一個關系
一般來說,我們在整合spring和SpringMVC這兩個框架中,web.xml會這樣寫到:
<!-- 加載spring容器 -->
<!-- 初始化加載application.xml的各種配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/application-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 配置springmvc前端控制器 -->
<servlet>
<servlet-name>taotao-manager</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- contextConfigLocation不是必須的, 如果不配置contextConfigLocation,
springmvc的配置文件默認在:WEB-INF/servlet的name+"-servlet.xml" -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
首先配置的是Spring容器的初始化加載的application文件,然后是SpringMVC的前端控制器(DispatchServlet),當配置完DispatchServlet后會在Spring容器中創建一個新的容器。其實這是兩個容器,Spring作為父容器,SpringMVC作為子容器。
讓我們用圖來看一下這個父子關系的原理:
平時我們在項目中注入關系是這樣的順序(結合圖來說):在Service中注入Dao(初始化自動注入,利用@Autowired),接着在Controller里注入Service(初始化自動注入,利用@Autowired),看圖,這就意味這作為SpringMVC的子容器是可以訪問父容器Spring對象的。
那么問大家一個問題。要是反過來呢,你把Controller注入到Service中能行么?
肯定是不行的啊!(如圖,這也說明了父容器是不能調用子容器對象的)
如果Dao,Serive,Controller要是都在Spring容器中,無疑上邊的問題是肯定的,因為都是在一個bean里,一個容器中。
2.問題:為什么不能在Spring中的Service層配置全局掃描?
例如:一個項目中我總項目的名字叫com.shop,我們在配置applicationContext-service.xml中,包掃描代碼如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
...../ 此處省略>
<!-- 掃描包Service實現類 -->
<context:component-scan base-package="com.shop.service"></context:component-scan>
</beans>
上面所配置的是一個局部掃描,而不是全局掃描。接下來說原因:
這里就和上面講到的父子容器有關系,假設我們做了全局掃描那么代碼如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
...../ 此處省略>
<!-- 掃描包Service實現類 -->
<context:component-scan base-package="com.shop"></context:component-scan>
</beans>
此時的Spring容器中就會掃描到@Controller,@Service,@Reposity,@Component,此時的圖如下:
結合圖去看,相當於他們都會放到大的容器中,而這時的SpringMVC容器中沒有對象,沒有對象就沒有Controller,所以加載處理器,適配器的時候就會找不到映射對象,映射關系,因此在頁面上就會出現404的錯誤。
3.如果不用Spring容器,直接把所有層放入SpringMVC容器中可不可以?
當然可以,如果沒有Spring容器,我們是可以把所有層放入SpringMVC的。單獨使用這個容器是完全可以的,而且是輕量級的。就是直接把所有的層次關系都放到了SpringMVC中,並沒有用到Spring容器。
4.那么為什么我們在項目中還要聯合用到Spring容器和SpringMVC容器?
答案是:Spring的擴展性,如果要是項目需要加入Struts等可以整合進來,便於擴展框架。如果要是為了快,為了方便開發,完全可以用SpringMVC框架。
5.結論
如果在項目中我們在Service層做全局包掃描,那么springmvc不能提供服務,因為springmvc子容器中沒有controller對象。