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_3_0.xsd"
id="WebApp_ID" version="3.0">
<display-name>CtrTimeOut</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<context-param>
<param-name>contextConfigLocation</param-name>
# spring的配置
<param-value>classpath:config/spring/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>controller</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
# springmvc的配置
<param-value>classpath:config/spring/spring-controller.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>controller</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
複製程式碼
可以看到xml配置方式藉助了了ContextLoaderListener來啟動
看下ContextLoaderListener的類定義
public class ContextLoaderListener extends ContextLoader implements ServletContextListener
複製程式碼
ContextLoaderListener實現了ServletContextListener
public interface ServletContextListener extends EventListener {
//容器初始化完成
public void contextInitialized(ServletContextEvent sce);
//容器停止
public void contextDestroyed(ServletContextEvent sce);
}
複製程式碼
由servlet標準可知ServletContextListener是容器的生命週期方法,springmvc就藉助其啟動與停止
ContextLoadListener呼叫了initWebApplicationContext方法,建立WebApplicationContext作為spring的容器上下文
#org.springframework.web.context.ContextLoader
/**
* 根據xml配置建立applicationContext
*/
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
...
try {
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
if (this.context == null) {//判空 (以註解方式配置時非空)
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
...
//讀取contextConfigLocation配置並refresh()
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
//將applicationContext設定到servletContext中
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
...
return this.context;
}
}
複製程式碼
而DispatcherServlet建立WebApplicationContext作為springmvc的上下文 並將ContextLoadListener建立的上下文設定為自身的parent
DispatcherServlet extends FrameworkServlet
#org.springframework.web.servlet.FrameworkServlet
@Override
protected final void initServletBean() throws ServletException {
...
try {
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
...
}
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
...
if (wac == null) {
//建立applicationContext
wac = createWebApplicationContext(rootContext);
}
...
return wac;
}
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
//XmlWebApplicationContext
Class<?> contextClass = getContextClass();
...
//建立applicationContext
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(getEnvironment());
//設定parent(ContextLoadListener中建立的applicationContext)
wac.setParent(parent);
//讀取contextConfigLocation配置
wac.setConfigLocation(getContextConfigLocation());
//refresh()
configureAndRefreshWebApplicationContext(wac);
return wac;
}
複製程式碼
springmvc的applicationContext會去讀取配置檔案 我們來看一個最簡單的配置檔案
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"
default-autowire="byName">
#springmvc容器掃描路徑
<context:component-scan base-package="com.iflytek.ossp.ctrtimeout.controller"></context:component-scan>
#spring4新增的標籤 主要是新增了預設的HandleMappin,ViewResolver,HandleAdapter
<mvc:annotation-driven />
</beans>
複製程式碼
springmvc標籤解析
根據spring的自定義schema解析機制 我們找到 在下圖位置
http\://www.springframework.org/schema/mvc=org.springframework.web.servlet.config.MvcNamespaceHandler
複製程式碼
可以看到mvc所有的標籤解析器都定義在此
public class MvcNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());
registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());
registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());
registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());
registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser());
registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser());
registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser());
registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("velocity-configurer", new VelocityConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser());
}
}
複製程式碼
來看一下AnnotationDrivenBeanDefinitionParser解析器做了什麼
解析過程較為複雜 通過註釋我們可以得知以下物件將被裝載
java方式
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer{
//載入spring配置 建立spring上下文
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] {RootConfig.class};
}
//載入springMvc配置 建立springMVC上下文
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] {WebConfig.class}; // 指定配置類
}
//DiapatchServlet對映路徑
@Override
protected String[] getServletMappings() {
return new String[] {"/"}; // 將dispatcherServlet對映到“/”
}
}
複製程式碼
可以看到,同xml方式相同,java方式啟動也有兩個上下文
同xml方式類似,java配置啟動方式也需要藉助於servlet容器的生命週期方法,就是WebApplicationInitializer介面
public interface WebApplicationInitializer {
//容器啟動時被呼叫
void onStartup(ServletContext servletContext) throws ServletException;
}
複製程式碼
注: WebApplicationInitializer是spring定義的介面,它能夠響應容器生命週期的原因是因為SpringServletContainerInitializer
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer{
//得到所有WebApplicationInitializer實現 並呼叫其onStartup()方法
@Override
public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();
...
for (Class<?> waiClass : webAppInitializerClasses) {
...
initializers.add((WebApplicationInitializer) waiClass.newInstance());
...
}
...
AnnotationAwareOrderComparator.sort(initializers);
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
}
//真正的容器生命週期方法
//會根據@HandlesTypes註解獲取所有類實現,並作為onStartup()方法的第一個引數
public interface ServletContainerInitializer {
public void onStartup(Set<Class<?>> c, ServletContext ctx)
throws ServletException;
}
複製程式碼
啟動過程:
- AbstractContextLoaderInitializer呼叫createRootApplicationContext建立spring上下文
- AbstractDispatcherServletInitializer呼叫createServletApplicationContext建立springMVC上下文
經過以上兩步spring容器就啟動起來了
再看xml方式通過mvc標籤來新增預設實現(HandlerMapping,HandlerAdapter等等)
java方式則通過@EnableWebMvc註解來新增預設實現
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
@Override
protected void configurePathMatch(PathMatchConfigurer configurer) {
this.configurers.configurePathMatch(configurer);
}
@Override
protected void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
this.configurers.configureContentNegotiation(configurer);
}
@Override
protected void configureAsyncSupport(AsyncSupportConfigurer configurer) {
this.configurers.configureAsyncSupport(configurer);
}
@Override
protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
this.configurers.configureDefaultServletHandling(configurer);
}
@Override
protected void addFormatters(FormatterRegistry registry) {
this.configurers.addFormatters(registry);
}
@Override
protected void addInterceptors(InterceptorRegistry registry) {
this.configurers.addInterceptors(registry);
}
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
this.configurers.addResourceHandlers(registry);
}
@Override
protected void addCorsMappings(CorsRegistry registry) {
this.configurers.addCorsMappings(registry);
}
@Override
protected void addViewControllers(ViewControllerRegistry registry) {
this.configurers.addViewControllers(registry);
}
@Override
protected void configureViewResolvers(ViewResolverRegistry registry) {
this.configurers.configureViewResolvers(registry);
}
@Override
protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
this.configurers.addArgumentResolvers(argumentResolvers);
}
@Override
protected void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
this.configurers.addReturnValueHandlers(returnValueHandlers);
}
@Override
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
this.configurers.configureMessageConverters(converters);
}
@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
this.configurers.extendMessageConverters(converters);
}
@Override
protected void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
this.configurers.configureHandlerExceptionResolvers(exceptionResolvers);
}
@Override
protected void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
this.configurers.extendHandlerExceptionResolvers(exceptionResolvers);
}
@Override
protected Validator getValidator() {
return this.configurers.getValidator();
}
@Override
protected MessageCodesResolver getMessageCodesResolver() {
return this.configurers.getMessageCodesResolver();
}
}
複製程式碼
@IMPORT是spring4的註解 用於關聯@Configuration類
DelegatingWebMvcConfiguration:像它的名字一樣只是個delegate,起作用的主要是其父類WebMvcConfigurationSupport.
WebMvcConfigurationSupport:其中註冊了大量的預設實現(如同AnnotationDrivenBeanDefinitionParser一樣),同時它的持有一個WebMvcConfigurerComposite物件.
WebMvcConfigurerComposite:內部聚合了WebMvcConfigurerAdapter的集合.
WebMvcConfigurerAdapter:用於新增自定義HandlerAdapter,HandlerMapping等
springMVC的啟動過程就是這樣了..