问题1
Controller被初始化两次。
配置文件
web.xml
<context-param> <param-name>webAppRootKey</param-name> <param-value>testApp</param-value> </context-param> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/classes/META-INF/spring/core/applicationContext.xml</param-value> </context-param> <listener> <listener-class>com.jc.site.web.SystemContextListener</listener-class> </listener> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/classes/META-INF/spring/core/dispatcher-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping>
applicationContext.xml
<context:annotation-config/> <context:component-scan base-package="com.jc.site" /> <bean id="propertyConfigurer" class="com.jc.site.common.util.PropertiesUtil"> <property name="locations"> <list> <value>classpath:jdbc.properties</value> <value>classpath:system.properties</value> <value>classpath:redis.properties</value> </list> </property> </bean> <import resource="classpath:META-INF/spring/db/jdbc/applicationContext_jdbc.xml"/> <import resource="classpath*:META-INF/spring/springContext_*.xml"/>
dispatcher-servlet.xml
<context:annotation-config /> <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"> <property name="order" value="1" /> </bean> <context:component-scan base-package="com.jc.site.controller" />
目标Controller代码
@Controller public class IndexController extends BaseController { public IndexController() { printStackTrace(); System.out.println("IndexController init....hashCode=" + this.hashCode()); } private static void printStackTrace() { StackTraceElement[] stackElements = new Throwable().getStackTrace(); if(stackElements != null) { System.out.println("--------------------------------------------------------"); for(int i = 0; i < stackElements.length; i++) { System.out.println("\t"+ stackElements[i]); } System.out.println("--------------------------------------------------------"); } } }
启动项目后的打印日志:
信息: Initializing Spring root WebApplicationContext -------------------------------------------------------- com.jc.site.controller.IndexController.printStackTrace(IndexController.java:59) com.jc.site.controller.IndexController.<init>(IndexController.java:21) …………………… org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:112) com.jc.site.web.SystemContextListener.contextInitialized(SystemContextListener.java:19) org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4973) org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5467) org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150) org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559) org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549) java.util.concurrent.FutureTask.run(FutureTask.java:262) java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) java.lang.Thread.run(Thread.java:745) -------------------------------------------------------- IndexController init....hashCode=2125761735 init UserDao... 2015-06-09 11:52:28,357 INFO (com.alibaba.druid.pool.DruidDataSource:652) - {dataSource-1} inited 六月 09, 2015 11:52:28 上午 org.apache.catalina.core.ApplicationContext log 信息: Initializing Spring FrameworkServlet 'dispatcher' -------------------------------------------------------- com.jc.site.controller.IndexController.printStackTrace(IndexController.java:59) com.jc.site.controller.IndexController.<init>(IndexController.java:21) …………………… org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:665) org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:521) org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:462) org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136) javax.servlet.GenericServlet.init(GenericServlet.java:158) org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1284) org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1197) org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1087) org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:5210) org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5493) org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150) org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559) org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549) java.util.concurrent.FutureTask.run(FutureTask.java:262) java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) java.lang.Thread.run(Thread.java:745) -------------------------------------------------------- IndexController init....hashCode=638958293 六月 09, 2015 11:52:28 上午 org.apache.coyote.AbstractProtocol start 信息: Starting ProtocolHandler ["http-apr-8080"] 六月 09, 2015 11:52:28 上午 org.apache.coyote.AbstractProtocol start 信息: Starting ProtocolHandler ["ajp-apr-8009"] 六月 09, 2015 11:52:28 上午 org.apache.catalina.startup.Catalina start 信息: Server startup in 3616 ms
看日志发现调用栈 第一个是ContextLoaderListener初始化生成的,第二个是由GenericServlet初始化生成的。
第二个GenericServlet.java引起的初始化调用栈中有
org.springframework.web.servlet.DispatcherServlet.initHandlerMappings(DispatcherServlet.java:525) org.springframework.web.servlet.DispatcherServlet.initStrategies(DispatcherServlet.java:440) org.springframework.web.servlet.DispatcherServlet.onRefresh(DispatcherServlet.java:429)
可见是初始化Spring MVC的DispatcherServlet引起的。
SpringMVC的启动创建了两个applicationContext。参见:http://blog.csdn.net/madun/article/details/8988860/
Controller属于MVC层的,而applicationContext.xml属于Spring的。component-scan默认会加载内容包含Controller。
component-scan Scans the classpath for annotated components that will be auto-registered as Spring beans. By default, the Spring-provided @Component, @Repository, @Service, and @Controller stereotypes will be detected.
所以只要在applicationContext的配置中剔除Controller即可。
<context:component-scan base-package="com.jc.site" > <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
启动,Controller只初始化一次,且功能正常!
参考文档
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/FilterType.html
http://www.leveluplunch.com/blog/2014/08/30/exclude-filter-component-scan-spring/
http://blog.csdn.net/liuwenbo0920/article/details/7260013
http://www.cnblogs.com/zemliu/p/3201112.html
【遗留需要确认的问题】
既然Spring,SpringMVC中生成了两个不同的ApplicationContext,那一个http请求Spring是如何在这两个ApplicationContext中查找、获取 相应的BEAN来响应 请求的。