SpringMVC建立過程
SpringMVC核心就是一個DispatcherServlet,所有請求的處理都是由它來做的,下面我們就來看一下它的建立過程:
Servlet建立時可以直接呼叫HttpServletBean中的無引數的init方法
public final void init() throws ServletException { if (logger.isDebugEnabled()) { logger.debug("Initializing servlet '" + getServletName() + "'"); } try { //將servlet中配置的引數設定到pvs中 PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); //建立要=一個DispatcherServlet的bean物件 BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); //做一些初始化功能 initBeanWrapper(bw); //將配置的引數設定到bw中 bw.setPropertyValues(pvs, true); } catch (BeansException ex) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); throw ex; } // 留給子類實現的模板方法,做一些初始化 initServletBean(); if (logger.isDebugEnabled()) { logger.debug("Servlet '" + getServletName() + "' configured successfully"); } }
在FrameworkServlet中實現了initServletBean方法:
protected final void initServletBean() throws ServletException { getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'"); if (this.logger.isInfoEnabled()) { this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started"); } long startTime = System.currentTimeMillis(); try { //初始化webApplicationContext this.webApplicationContext = initWebApplicationContext(); //初始化FrameworkServlet initFrameworkServlet(); } catch (ServletException ex) { this.logger.error("Context initialization failed", ex); throw ex; } catch (RuntimeException ex) { this.logger.error("Context initialization failed", ex); throw ex; } if (this.logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " + elapsedTime + " ms"); }
在這個方法中主要做了兩件事:
1、初始化了webApplicationContext
2、初始化了FrameworkServlet
下面我們就來詳細看一下這兩個過程
protected WebApplicationContext initWebApplicationContext() { //獲取rootContext WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; //如果已經通過構造方法設定裡webApplicationContext if (this.webApplicationContext != null) { wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { if (cwac.getParent() == null) { cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { //當webApplicationContext已經存在ServletContext中時,通過配置在Servlet中ContextAttribute引數獲取 wac = findWebApplicationContext(); } if (wac == null) { //建立一個webApplicationContext wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { //當ContextRefreshedEvent事件沒有觸發時呼叫此方法,可以在子類中重寫 onRefresh(wac); } if (this.publishContext) { //將ApplicationContext設定到ServeltContext中 String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); if (this.logger.isDebugEnabled()) { this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() + "' as ServletContext attribute with name [" + attrName + "]"); } } return wac; }
總結一下這個方法,一共做了三件事:
1、獲取spring的根容器rootContext,預設情況下spring會將自己的容器設定成ServletContext的屬性
2、設定webApplicationContext並根據情況呼叫onRefresh方法
3、將webApplicationContext設定到ServletContext中
在設定webApplicationContext時一共有三種方法
1、在構造方法中已經傳遞webApplicationContext引數
2、webApplicationContext已經存在ServletContext中了,這時只需要配置Servlet的時候獲取屬性
3、第三種是自己建立一個:
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
//獲取建立型別
Class<?> contextClass = getContextClass();
...
//檢查建立型別
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException(
"Fatal initialization error in servlet with name '" + getServletName() +
"': custom WebApplicationContext class [" + contextClass.getName() +
"] is not of type ConfigurableWebApplicationContext");
}
//具體建立
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
//設定環境
wac.setEnvironment(getEnvironment());
//設定父容器
wac.setParent(parent);
//將設定的配置檔案路徑儲存起來
wac.setConfigLocation(getContextConfigLocation());
configureAndRefreshWebApplicationContext(wac);
return wac;
}
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
if (this.contextId != null) {
wac.setId(this.contextId);
}
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(getServletContext().getContextPath()) + "/" + getServletName());
}
}
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
//新增監聽器
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
//獲取環境
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
postProcessWebApplicationContext(wac);
applyInitializers(wac);
wac.refresh();
}
在建立過程中首先獲取要建立的型別,然後檢查是否是ConfigurableWebApplicationContext型別,如果不屬於就丟擲異常。接下來呼叫instantiateClass建立,建立完後會進行配置,在configureAndRefreshWebApplicationContext方法中添加了監聽器,這個監聽器可以根據輸入的引數進行選擇,實際監聽的是ContextRefreshListener監聽的事件:
private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
FrameworkServlet.this.onApplicationEvent(event);
}
}
這個監聽會呼叫onApplicationEvent方法:
public void onApplicationEvent(ContextRefreshedEvent event) {
this.refreshEventReceived = true;
onRefresh(event.getApplicationContext());
}
在處理時首先會將refreshEventReceived 設定為true,然後呼叫一次onRefresh方法,這個方法是DispatcherServlet的入口:
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
在這個方法中呼叫了initStrategies來做初始化操作
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
在這個方法裡面初始化了9個元件,下面我們就以LocaleResolver為例來看一下是如何初始化的:
private void initLocaleResolver(ApplicationContext context) {
try {
//從context中獲取對應的bean
this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
if (logger.isDebugEnabled()) {
logger.debug("Using LocaleResolver [" + this.localeResolver + "]");
}
}
catch (NoSuchBeanDefinitionException ex) {
// 獲取預設的元件
this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate LocaleResolver with name '" + LOCALE_RESOLVER_BEAN_NAME +
"': using default [" + this.localeResolver + "]");
}
}
}
首先會在容器裡面查詢對應的bean,如果找不到就會使用預設的元件:
protected <T> T getDefaultStrategy(ApplicationContext context, Class<T> strategyInterface) {
List<T> strategies = getDefaultStrategies(context, strategyInterface);
if (strategies.size() != 1) {
throw new BeanInitializationException(
"DispatcherServlet needs exactly 1 strategy for interface [" + strategyInterface.getName() + "]");
}
return strategies.get(0);
}
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
//獲取策略的名字
String key = strategyInterface.getName();
//根據key獲取型別
String value = defaultStrategies.getProperty(key);
if (value != null) {
//可能有多個名字,將他們分割成陣列
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList<T>(classNames.length);
//載入所有的策略的物件
for (String className : classNames) {
try {
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
Object strategy = createDefaultStrategy(context, clazz);
strategies.add((T) strategy);
}
catch (ClassNotFoundException ex) {
throw new BeanInitializationException(
"Could not find DispatcherServlet's default strategy class [" + className +
"] for interface [" + key + "]", ex);
}
catch (LinkageError err) {
throw new BeanInitializationException(
"Error loading DispatcherServlet's default strategy class [" + className +
"] for interface [" + key + "]: problem with class file or dependent class", err);
}
}
return strategies;
}
else {
return new LinkedList<T>();
}
}
最終會執行ClassUtils.forName來建立策略,它的引數是className,那麼我們就來看看className是怎麼來的:
private static final Properties defaultStrategies;
static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
try {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage());
}
}
我們發現defaultStrategies是從DEFAULT_STRATEGIES_PATH裡面加載出來的資源:
# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
在這個檔案裡面儲存了一些預設的元件的類
到這SpringMVC的建立過程就簡單分析完了,它的建立順序從HeepServletBean、FrameworkServlet到DispathcherServlet。