SpringMVC工作原理流程(一)

一、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方法主要做了三件事:

  1. 獲取Spring的根容器rootContext
  2. 設置webApplicationContext並根據情況調用onRefresh方法
  3. 將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個組件。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章