Hello World

吞风吻雨葬落日 欺山赶海踏雪径

0%

Spring MVC项目中Controller初始化两次的问题分析

问题1:Controller被初始化两次。

配置文件

web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<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

1
2
3
4
5
6
7
8
<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代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@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("--------------------------------------------------------");
}
}
}

启动项目后的打印日志:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
信息: 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引起的初始化调用栈中有

1
2
3
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即可。

1
2
3
<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来响应 请求的。