一、SpringMVC整體結構
下面是SpirngMVC核心Servlet的繼承結構圖
Servlet的繼承結構一共有五個類,GenericServelt,HttpServlet,這兩個類的介紹:傳送門,剩下三個類HttpServletBean,FrameworkServlet,和DispatcherServlet是SpringMVC框架的類。
EnviornmentCapable介紹
這個接口顧名思義,具有Enviornment能力。當Spring需要Enviornment,實現getEnvironment方法就可以獲得Enviornment。
Aware的介紹
Aware這個接口裏面沒有內容,XXXAware在Spring裏標識對XXX可以感知。容器管理的Bean一般不需要了解容器的狀態和直接使用容器,但是在某些情況下,是需要在Bean中直接對IOC容器進行操作的,這時候,就需要在Bean中設定對容器的感知。Spring IOC容器也提供了該功能,它是通過特定的Aware接口來完成的。
通俗的解釋就是:如果在某個類裏面想要使用Spring的內容,就可以實現XXXAware接口告訴Spring,Spring看到後就給你送過來,而接受的方式是通過實現唯一的方法setXXX。當然實現XXXAware接口的類需要交給Spring管理。
看下面代碼
@Component
public class AppTest implements ApplicationContextAware, EnvironmentAware {
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
//實現了這個方法Spring會把applicationContext對象傳進來
System.out.println(applicationContext);
}
public void setEnvironment(Environment environment) {
//實現了這個方法Spring會把Environment對象傳進來
System.out.println(environment);
}
}
environment對象封裝了ServletContext、還封裝了ServletConfig、JndiProperty、系統環境變量、系統屬性。這些都封裝到了propertySources屬性裏面。
二、SpringMVC實現流程的涉及的類
1、HttpServletBean類
HttpSerlvetBean繼承自HttpServlet,重寫了無參的init方法。
Servlet創建時候會先調用有參的init()方法,在有參的init方法內調用了無參的init方法。
public final void init() throws ServletException {
//日誌省略不寫
// ServletConfigPropertyValues是HttpServletBean內部靜態類
//構造過程中會使用ServletConfig對象找出web.xml配置文件中的配置參數並且設置到ServletConfigPropertyValues內部
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
//使用BeanWrapper構造DispatherServlet
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
//使用屬性編輯器
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
//模板方法,可以在子類調用,做一些初始化工作,bw代表DispatcherServlet,springMVC未做任何操作。
initBeanWrapper(bw);
//將配置的初始化值(如contextConfigLocation)設置到DispatherServlet
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
//日誌省略不寫
throw ex;
}
}
//FrameworkServelt的初始化入口
initServletBean();
//日誌省略不寫
}
2、FrameworkServlet類
initServletBean();
從HttpServletBean中可知,FrameworkServlet的初始化入口是initServletBean();
@Override
protected final void initServletBean() throws ServletException {
//省略無關代碼及日誌
//初始化WebApplicationContext屬性
//WbeApplicationContext是繼承自ApplicationContext接口的接口
//該屬性也是Spring容器上下文,ServletFramework類的作用是讓Servlet和Spring容器關聯
this.webApplicationContext = initWebApplicationContext();
//該方法主要是爲了讓子類覆蓋並做一些初始化工作
//DisPatcherServlet並未覆蓋改方法
initFrameworkServlet();
//省略無關代碼及日誌
}
initWebApplicationContext();
protected WebApplicationContext initWebApplicationContext() {
//從servletContext中獲取根上下文
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
//DispatherServlet類中有一個以webApplicationContext的構造器
if (this.webApplicationContext != null) {
//如果已經通過構造方法設置了webApplicationContext,那將使當前這個。
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
//如果上下文尚未刷新,那麼將設置父上下文
if (!cwac.isActive()) {
//這個webApplicationContext沒有父上下文
if (cwac.getParent() == null) {
//設置父上下文,可以爲空
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
//當webApplicationContext已經存在ServletContext中,這種方式創建WebApplicationContext那麼會調用onRefresh方法
//可以通過在Servlet的init-param中配置
//<param-name>contextAttribute</param-name>
//<param-value>值</param-value>
//一般不會設置contextAttribute的值
//所以會返回爲null
wac = findWebApplicationContext();
}
//如果webApplicationContext還沒有創建,則創建一個
if (wac == null) {
//創建webApplication並設置根上下文爲父上下文
//然後配置ServletConfig,serveltContext實例到這個上下文
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
onRefresh(wac);
}
if (this.publishContext) {
//將新創建的上下文保存到ServletContext中
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
//省略日誌
}
return wac;
}
createWebApplicationContext()
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
//這個contextClass是XmlWebApplicationContext.class
Class<?> contextClass = getContextClass();
//判斷ConfigurableWebApplicationContext.class是不是contextClass的父類或者接口
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
//拋出異常
}
//實例化ConfigurableWebApplicationContext對象
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
//設置Environment
wac.setEnvironment(getEnvironment());
//設置父上下文
wac.setParent(parent);
//設置SpringMVC配置文件,如果沒有設置,默認傳入WEB-INFO/[ServletName]-Servlet.xml
wac.setConfigLocation(getContextConfigLocation());
//配置和刷新WebApplicationContext
configureAndRefreshWebApplicationContext(wac);
return wac;
}
configureAndRefreshWebApplicationContext()
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac){
if(ObjectUtils.identityToString(wac).equals(wac.getId())){
if(this.contextId!=null){
wac.setId(this.contextId);
}
else{
//根據可用信息分配更有用的ID
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX+
ObjectUtils.getDisplayString(getServletContext().getContextPath())+'/'+getServletName());
}
}
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
//添加監聽ContextRefreshListener的監聽器,ContextRefreshListener是FrameworkServlet的內部類
wac.addApplicationListener(new SourceFilteringListener(wac,new ContextRefreshListener()));
ConfigurableEnvironment env=wac.getEnvironment();
if(env instanceof ConfigurableWebEnvironment){
((ConfigurableWebEnvironment)env).initPropertySources(getServletContext(),getServletConfig());
}
postProcessWebApplicationContext(wac);
applyInitializers(wac);
//初始化springMVC各種的相關配置,各種注入的Controller,配置文件等
wac.refresh();
}
ContextRefreshListener類
private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
FrameworkServlet.this.onApplicationEvent(event);
}
}
public void onApplicationEvent(ContextRefreshedEvent event) {
this.refreshEventReceived = true;
//調用子類的onRefresh
onRefresh(event.getApplicationContext());
}
initWebApplicationContext方法主要做了三件事:
- 獲取Spring的根容器rootContext
- 設置webApplicationContext並根據情況調用onRefresh方法
- 將webApplicationContext設置到ServletContext中。
FrameworkServlet在構建的過程中主要作用就是初始化webApplicationContext屬性
如果在項目中有Spring框架,也讓Servlet和Spring容器做關聯。
DispatcherServelt類
onRefresh方式是DispatcherServlet的入口方法。onRefresh中簡單地調用了initStrategies,在initStrategies中調用了9個初始化組件方法。
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
初始化分兩步
private void initLocaleResolver(ApplicationContext context) {
try { //在context中獲取
this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
//日誌
}
catch (NoSuchBeanDefinitionException ex) {
// 使用默認組件
this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
//日誌
}
}
1、首先通過context.getBean()在容器裏面按註冊時的名稱或類型進行查找,只需在SpringMVC的配置文件中,配置相應類型的組件,容器就可以自動查找。
2、如果查找不到就調用getDefaultStrategy按照類型獲取默認組件。
三、總結
這裏主要分析了SpringMVC自身創建的過程,SpringMVC中Servlet一共有三個層次,分別是HttpServletBean,FrameworkServlet和DispatchServlet。
HttpServletBean直接繼承Java的HttpServlet,作用是將Servlet中配置的參數設置到相應的屬性。
FrameworkServlet作用初始化webApplicationContext。
DispatchServlet作用:初始化自身的9個組件。