1.Spring MVC整體結構介紹
Spring MVC主要類結構圖
image
從上圖中可以看到在Servlet的繼承結構中一共有5個類油够,分別是Java提供的兩個類GenericServlet,HttpServlet和Spring MVC提供的三個類HttpServletBean蜗细、FrameworkServlet和Dispat
cherServlet
Spring MVC請求流程圖
image
從上圖可以看出DispatcherServlet是Spring MVC框架的核心瓮具,通過DispatcherServlet獲取處理請求的Handler
Adapter(處理請求的具體Controller),然后將返回的結果交給ViewResolver(處理視圖的類),然后渲染出視圖返回給客戶端
2.HttpServletBean
通過前面的文章晨雳,我們知道搭建SpringMVC框架的時候在web.xml文件中配置了DispatcherServlet,它是Spring MVC控制器的核心奸腺,當系統(tǒng)開始運行時餐禁,DispatcherServlet就開始初始化(<load-on-startup>1</load-on-startup>),由于DispatcherServlet類本身沒有init初始化方法突照,它的父類中只有HttpServletBean中有inti初始化方法,HttpServletBean的初始化方法如下:
public final void init() throws ServletException {
if(this.logger.isDebugEnabled()) {
this.logger.debug("Initializing servlet \'" + this.getServletName() + "\'");
}
try {
//將Servlet中配置的參數封裝到ex變量中帮非,requiredProperties為必須參數,如果沒有配置則報異常
HttpServletBean.ServletConfigPropertyValues ex = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
BeanWrapper bw =PropertyAccessorFactory.forBeanPropertyAccess(this);
ServletContextResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
//模板方法讹蘑,調用的是FrameworkServlet中的初始化方法末盔,做一些初始化工作。bw代表DispatcherServlet
this.initBeanWrapper(bw);
//將配置的初始化值(如ContextConfigLocation)設置到DispatcherServlet中
bw.setPropertyValues(ex, true);
} catch (BeansException var4) {
this.logger.error("Failed to set bean properties on servlet \'" + this.getServletName() + "\'", var4);
throw var4;
}
//模板方法座慰,FrameworkServlet初始化的入口方法
this.initServletBean();
if(this.logger.isDebugEnabled()) {
this.logger.debug("Servlet \'" + this.getServletName() + "\' configured successfully");
}
}
HttpServletBean.ServletConfigPropertyValues ex = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
//getServletConfig()方法是Servlet接口提供的方法用于獲取ServletConfig陨舱,當Servlet的init方法被調用時,會接受到一個ServletConfig類型的參數版仔,指的是Servlet的配置游盲,在web.xml中定義Servlet時通過init-param標簽配置的參數就是通過ServletConfig保存的。
BeanWrapper 是Spring提供的一個用來操作JavaBean屬性的工具蛮粮,使用它可以直接修改一個對象的屬性
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
//通過PropertyAccessorFactory對象的forBeanPropertyAccess方法將DispatcherServlet封裝成BeanWrapper對象益缎,這樣通過bw變量就可以對DispatcherServlet的屬性進行操作
3.FrameworkServlet
從上面HttpServletBean中的初始化代碼得知,FrameworkServlet的初始化入口方法是initServletBean
protected final void initServletBean() throws ServletException {
this.getServletContext().log("Initializing Spring FrameworkServlet \'" + this.getServletName() + "\'");
if(this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet \'" + this.getServletName() + "\': initialization started");
}
long startTime = System.currentTimeMillis();
try {
//初始化WebApplicationContext
this.webApplicationContext = this.initWebApplicationContext();
//初始化FrameworkServlet
this.initFrameworkServlet();
} catch (ServletException var5) {
this.logger.error("Context initialization failed", var5);
throw var5;
} catch (RuntimeException var6) {
this.logger.error("Context initialization failed", var6);
throw var6;
}
if(this.logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
this.logger.info("FrameworkServlet \'" + this.getServletName() + "\': initialization completed in " + elapsedTime + " ms");
}
}
首先查看WebApplicationContext初始化代碼然想;WebApplicationContext是web應用的上下文環(huán)境
protected WebApplicationContext initWebApplicationContext() {
//通過getServletContext獲取的ServletContext來獲取根上下文
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
WebApplicationContext wac = null;
//如果已經通過構造方法設置了webApplicationContext
if(this.webApplicationContext != null) {
wac = this.webApplicationContext;
if(wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext attrName = (ConfigurableWebApplicationContext)wac;
if(!attrName.isActive()) {
if(attrName.getParent() == null) {
attrName.setParent(rootContext);
}
this.configureAndRefreshWebApplicationContext(attrName);
}
}
}
if(wac == null) {
//如果webApplicationContext已經存在ServletContext中時莺奔,通過配置在Servlet中的參數獲取
wac = this.findWebApplicationContext();
}
if(wac == null) {
//創(chuàng)建一個新的webApplicationContext
wac = this.createWebApplicationContext(rootContext);
}
if(!this.refreshEventReceived) {
//模板方法,HttpServletBean中用來初始化Spring MVC的九大組件
this.onRefresh(wac);
}
if(this.publishContext) {
//將ApplicationContext保存到ServletContext中
String attrName1 = this.getServletContextAttributeName();
this.getServletContext().setAttribute(attrName1, wac);
if(this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet \'" + this.getServletName() + "\' as ServletContext attribute with name [" + attrName1 + "]");
}
}
return wac;
}
獲取spinrg的根容器rootContext
WebApplicationContext rootContext=WebApplicationContextUtils.getWebApplicationContext(this.getServl
etContext());
設置webApplicationContext并根據情況調用onRefresh方法
設置webApplicationContext的三種方法
- 在構造方法中已經傳遞了webApplicationContext參數变泄,這時只需要對其進行一些設置即可
- webApplicationContext已經在ServletContext中了弊仪,這時只需要在配置Servlet的時候將ServletContext中的webApplicationContext的name配置到contextAttribute屬性就可以了
- 前面兩種方式都無效的情況下熙卡,通過createWebApplicationContext來創(chuàng)建一個
創(chuàng)建webApplicationContext的方法
protected WebApplicationContext createWebApplicationContext(
ApplicationContext parent) {
//通過getContextClass獲取要創(chuàng)建的類型
Class contextClass = this.getContextClass();
if(this.logger.isDebugEnabled()) {
this.logger.debug("Servlet with name \'" + this.getServletName() + "\' will try to create custom WebApplicationContext context of class \'" + contextClass.getName() + "\'" + ", using parent context [" + parent + "]");
}
if(!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Fatal initialization error in servlet with name \'" + this.getServletName() + "\': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext");
} else {
//具體創(chuàng)建
ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(this.getEnvironment());
wac.setParent(parent);
//將設置的contextConfigLocation參數傳給wac,默認傳入WEB-INF/[ServletName]-servlet.xml
wac.setConfigLocation(this.getContextConfigLocation());
this.configureAndRefreshWebApplicationContext(wac);
return wac;
}
}
將webApplicationContext設置到ServletContext中
if(this.publishContext) {
//將ApplicationContext保存到ServletContext中
String attrName1 = this.getServletContextAttributeName();
this.getServletContext().setAttribute(attrName1, wac);
if(this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet \'" + this.getServletName() + "\' as ServletContext attribute with name [" + attrName1 + "]");
}
}
根據publishContext標志判斷是否將webApplicationContext設置到ServletContext的屬性中励饵,publishContext標志可以在配置Servlet時通過init-param參數進行設置驳癌,HttpServletBean初始化時會將其設置到publishContext參數
3.DispatcherServlet
onRefresh方法是DispatcherServlet的入口方法
protected void onRefresh(ApplicationContext context) {
this.initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
this.initMultipartResolver(context);
this.initLocaleResolver(context);
this.initThemeResolver(context);
this.initHandlerMappings(context);
this.initHandlerAdapters(context);
this.initHandlerExceptionResolvers(context);
this.initRequestToViewNameTranslator(context);
this.initViewResolvers(context);
this.initFlashMapManager(context);
}
onRefresh方法調用了initStrategies方法,initStrategies方法中初始化了Spring MVC九大組件
4.小結
上面主要分析了Spring MVC自身的創(chuàng)建過程役听,Spring MVC中的Servlet一共分為三個層
次颓鲜,分別是HttpServletBean、FrameworkServlet和DispatcherServlet典予。
- HttpServletBean繼承自Java的HttpServlet甜滨,其作用是將Servlet中配置的參數設置到相應的屬性
- FrameworkServlet初始化了WebApplicationContext
- DispatcherServlet初始化了自身的9大組件