Table of Contents
三、Root WebApplicationContext和Servlet WebApplicationContext
一、配置
org.springframework.web.servlet.DispatcherServlet的配置除了常规的配置方法以外,还可以使用ContextLoaderListener监听器进行配置,如下:
第11-14行:contextConfigLocation用于指定Spring的配置文件
第15-17行: 配置spring核心监听器,默认会以 /WEB-INF/applicationContext.xml作为配置文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>springsun</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:app*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextAttribute</param-name>
<param-value>org.springframework.web.context.WebApplicationContext.ROOT</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
二、原理
1.监听器中执行原理
首先进入ContextLoaderListener类中,因为该类实现了javax.servlet.ServletContextListener接口,所以Tomcat发布Web应用时执行该类contextInitialized方法:
该方法中执行了initWebApplicationContext方法,参数为ServletContext类对象,即application对象
再进入initWebApplicationContext方法中,该方法是ContextLoaderListener继承自父类ContextLoader的方法:
第295行:判断ServletContext对象,即application对象是否已经存放了XmlWebApplicationContext实例,其中常量ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE为WebApplicationContext全类名与“.ROOT”拼接而成的字符串,即org.springframework.web.context.WebApplicationContext.ROOT;
第312行:createWebApplicationContext方法利用反射的方式创建XmlWebApplicationContext对象,并赋值给context变量;
第314行:由于XmlWebApplicationContext类间接实现了ConfigurableWebApplicationContext接口,所以该处if条件为true;
第315行:将context变量中XmlWebApplicationContext变量强制类型转换为ConfigurableWebApplicationContext类型;
第316行:由于cwac变量中对象尚未加载配置文件,所以进入代码块;
第325行:加载配置文件;
第328行:将context变量中的XmlWebApplicationContext对象保存至application内置对象中,保存的变量名即为org.springframework.web.context.WebApplicationContext.ROOT;
第347行:返回XmlWebApplicationContext示例对象;
接下来再来分析上面第325行中的configureAndRefreshWebApplicationContext方法的源代码:
第428行:为wac绑定ServletContext对象
第429行:此处常量CONFIG_LOCATION_PARAM为字符串contextConfigLocation,即获取web.xml配置文件中context-param标签key为contextConfigLocation的value值,上述配置中该value值为classpath:app*.xml
2.DispatchServlet加载时执行原理
DispatchServlet加载完整过程见博客:SpringMVC中load-on-startup标签的执行过程
Tomcat发布Web应用时依次执行init方法—>initServletBean方法—>执行initWebApplicationContext方法:
第549行:findWebApplicationContext用于在application内置对象中寻找已有的WebApplicationContext对象;
第553行:如果此时wac为null则说明第549行在application内置对象中未找到WebApplicationContext对象,则重新创建一个WebApplicationContext对象;
接下来进入findWebApplicationContext方法分析源码:
第587行:将web.xml配置文件中配置的DispatchServlet时配置的param-name标签key为contextAttribute的value值,此处为配置的org.springframework.web.context.WebApplicationContext.ROOT
第591-592行:getWebApplicationContext方法用于获取application内置对象中保存到的XmlWebApplicationContext对象
接下来再进入getWebApplicationContext方法分析源码:
第109行:attrName即为传入的org.springframework.web.context.WebApplicationContext.ROOT字符串,调用getAttribute方法即可获取application内置对象中保存的XmlWebApplicationContext对象;
第125行:将得到的XmlWebApplicationContext对象返回;
三、Root WebApplicationContext和Servlet WebApplicationContext
前面例子我们将Spring相关配置和SpringMVC相关配置一股脑写在了一个xml配置文件,试想如果需要配置的东西很多,那么该文件势必很长,势必很乱,为了解决这一诟病需要按照配置的不同创建两个配置文件:一个配置文件专门用于Spring相关配置;一个配置文件专门用于SpringMVC相关配置,如下:
root-context.xml:该文件仅用于实例化Service层和Dao层,用于数据库相关配置:
<?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:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
xsi:schemaLocation="http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!--仅仅实例化被@Component、@Service或@Repository等注解所修饰的类 -->
<context:component-scan base-package="com.jd">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" lazy-init="false" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" p:dataSource-ref="dataSource"></bean>
<tx:annotation-driven/>
<bean class="org.mybatis.spring.SqlSessionFactoryBean" p:dataSource-ref="dataSource" p:configLocation="classpath:mybatis-config.xml"
p:mapperLocations="classpath:/sql/*.xml">
</bean>
<mybatis-spring:scan base-package="com.jd.*.dao" />
</beans>
servlet-context.xml:该文件仅用于实例化Controller层,用于视图解析器等SpringMVC相关配置:
<?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 仅实例化被@Controller等注解所修饰的类 -->
<context:component-scan base-package="com.jd" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" p:defaultEncoding="UTF-8" p:maxUploadSize="10241000">
</bean>
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
此时在web.xml配置文件中就需要对root-context.xml和servlet-contxt.xml文件分别进行配置:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>cassini</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:root-context.xml</param-value>
</context-param>
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>