SpringMVC源码解析笔记(1)

**注:为了代码清晰易读,分析代码的过程将会删去日志实现以及异常处理(这些代码也很重要,具体可以看源码)。

1.ContextLoaderListener

初始接口java.util.EventListener,是个空接口,标志着这是个事件监听器。
实现了接口javax.servlet.ServletContextListener,能够监听ServletContext对象的生命周期,实际上就是监听Web应用的生命周期。Servlet容器启动或者终止Web应用的时候,就会触发ServletContextEvent事件,该事件就会由ServletContextListener来处理,其中ServletContextListener中定义了两个方法:
public void contextInitialized(ServletContextEvent sce);当Servlet容器启动Web应用时调用该方法。在调用完该方法之后,容器再对Filter初始化,并且对那些在Web应用启动时就需要被初始化的Servlet进行初始化。
public void contextDestroyed(ServletContextEvent sce);当Servlet容器终止Web应用时调用该方法。在调用该方法之前,容器会先销毁所有的Servlet和Filter过滤器。
继承了org.springframework.web.context.ContextLoader:
①defaultStrategies静态属性
按照默认路径加载配置Properties对象
private static final String DEFAULT_STRATEGIES_PATH = “ContextLoader.properties”;
private static final Properties defaultStrategies;
static {
try {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
} catch (IOException ex) {
throw new IllegalStateException("Could not load ‘ContextLoader.properties’: " + ex.getMessage());
}
}
其中配置文件就一行:
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
说明了如果没有指定那么用的WebApplicationContext类型就是XmlWebApplicationContext类。
②context 属性,Root WebApplicationContext 对象
@Nullable
private WebApplicationContext context;
public ContextLoader() {
}
public ContextLoader(WebApplicationContext context) {
this.context = context;
}
③initWebApplicationContext

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
//如果ServletContext中已经有了RootApplicationContext,就抛出错误。
   if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
      throw new IllegalStateException(
            "Cannot initialize context because there is already a root application context present - " +
            "check whether you have multiple ContextLoader* definitions in your web.xml!");
   }
//记录开始时间
   long startTime = System.currentTimeMillis();
   try {
//判断成员变量的WebApplicationContext是不是空的,如果是就创建对象
      if (this.context == null) {
         this.context = createWebApplicationContext(servletContext);
      }
//判断是否是ConfigurationWebApplicationContext的子类
      if (this.context instanceof ConfigurableWebApplicationContext) {
         ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
//判断是否已经激活/刷新
         if (!cwac.isActive()) {
//如果没有父容器就加载并且设置父容器
            if (cwac.getParent() == null) {
            //空实现
               ApplicationContext parent = loadParentContext(servletContext);
               cwac.setParent(parent);
            }
//没激活的就重新设置并且激活
            configureAndRefreshWebApplicationContext(cwac, servletContext);
         }
      }
 //将rootWebApplicationContext存入ServletContext
      servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
     //从线程中取出线程上下文类加载器
      ClassLoader ccl = Thread.currentThread().getContextClassLoader();
      //如果线程上下文类加载器就是当前类的类加载,就把类变量的context指给currentContext
      if (ccl == ContextLoader.class.getClassLoader()) {
         currentContext = this.context;
      }
      //存入map中,键是当前线程的类加载器,值是context
      else if (ccl != null) {
         currentContextPerThread.put(ccl, this.context);
      }
      return this.context;
   }
   catch (RuntimeException ex) {
      logger.error("Context initialization failed", ex);
      servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
      throw ex;
   }
   catch (Error err) {
      logger.error("Context initialization failed", err);
 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
      throw err;
   }
}

④createWebApplicationContext

	protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
	//确定Context的类
		Class<?> contextClass = determineContextClass(sc);
		//如果确定的类型不是ConfigurableWebApplicationContext的子类,抛出错误
		if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
			throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
					"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
		}
		//接着通过反射创建context类的对象,强转成ConfigurableWebApplicationContext
		return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
	}

⑤determineContextClass

protected Class<?> determineContextClass(ServletContext servletContext) {
//首先从ServletContext中获取设置的ContextClass的值
		String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
		if (contextClassName != null) {
			try {
			//如果有设置的值,就直接返回类,类加载是线程上下文下载器(如果设置过的),没有的话就是当前类的类加载器
				return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
			}
			catch (ClassNotFoundException ex) {
				throw new ApplicationContextException(
						"Failed to load custom context class [" + contextClassName + "]", ex);
			}
		}
		else {
		//如果没有设置过,就从defaultStrategies获取类,即 XmlWebApplicationContext 类
			contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
			try {
				return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
			}
			catch (ClassNotFoundException ex) {
				throw new ApplicationContextException(
						"Failed to load default context class [" + contextClassName + "]", ex);
			}
		}
	}

⑥configureAndRefreshWebApplicationContext

	protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
	//如果使用的是默认id,就重新设置id
		if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
			String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
			//如果设置了contextid 就直接使用,用<context-param>在web.xml中设置的
			if (idParam != null) {
				wac.setId(idParam);
			}
			else {
			//否则自动生成
				wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
						ObjectUtils.getDisplayString(sc.getContextPath()));
			}
		}
		//给context设置servletcontext属性
		wac.setServletContext(sc);
		//从servletcontext中取出储存的configlocation,例如
		//<context-param>
        // <param-name>contextConfigLocation</param-name>
        //<param-value>classpath:config/applicationContext.xml</param-value>
       //</context-param>
		String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
		//如果有储存的就赋值给wac
		if (configLocationParam != null) {
			wac.setConfigLocation(configLocationParam);
		}
		//不知道环境什么用,这段先忽略
		ConfigurableEnvironment env = wac.getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {
			((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
		}
		
		customizeContext(sc, wac);
		//刷新 context ,执行初始化,这是ioc中的内容,简单点描述就是根据applicationContext.xml中配置的内容生成一个容器,称为rootXmlApplicationContext。
		wac.refresh();
	}

⑦closeWebApplicationContext

public void closeWebApplicationContext(ServletContext servletContext) {
	servletContext.log("Closing Spring root WebApplicationContext");
	try {
	//关闭context
		if (this.context instanceof ConfigurableWebApplicationContext) {
			((ConfigurableWebApplicationContext) this.context).close();
		}
	}
	finally {
	//从currentContext,currentContextPerThread和servletContext移出
		ClassLoader ccl = Thread.currentThread().getContextClassLoader();
		if (ccl == ContextLoader.class.getClassLoader()) {
			currentContext = null;
		}
		else if (ccl != null) {
			currentContextPerThread.remove(ccl);
		}
		servletContext.removeAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
	}
}

接下来分析ContextLoaderListener。
构造方法:
public ContextLoaderListener() {}
public ContextLoaderListener(WebApplicationContext context) {
super(context);}
第二个构造方法是传入一个WebApplicationContext,这实际上就不再需要再创建一个新的WebApplicationContext对象。

public void contextInitialized(ServletContextEvent event) {
   initWebApplicationContext(event.getServletContext());
}

在容器启动时运行,作用是初始化WebApplicationContext。具体实现在org.springframework.web.context.ContextLoader。上文已经分析。

@Override
public void contextDestroyed(ServletContextEvent event) {
   closeWebApplicationContext(event.getServletContext());
   ContextCleanupListener.cleanupAttributes(event.getServletContext());
}

销毁 WebApplicationContext 容器的逻辑,关闭并且清理WebApplicationContext。

2.DispatcherServlet

在这里插入图片描述
可以看到左边的关系是关于spring的,右边的是关于servlet的。
HttpServletBean ,负责将 ServletConfig 设置到当前 Servlet 对象中。
FrameworkServlet ,负责初始化 Spring Servlet WebApplicationContext 容器。
DispatcherServlet ,负责初始化 Spring MVC 的各个组件,以及处理客户端的请求。
首先我们知道在创建servlet对象后会调用servlet对象的init方法进行初始化,DispatcherServlet中也有init方法,具体的实现在HttpServletBean类中:
首先看看HttpServletBean的两个成员变量:
@Nullable
private ConfigurableEnvironment environment;
关于environment相关的方法:

@Override // 实现自 EnvironmentAware 接口,自动注入
public void setEnvironment(Environment environment) {
	Assert.isInstanceOf(ConfigurableEnvironment.class, environment, "ConfigurableEnvironment required");
	this.environment = (ConfigurableEnvironment) environment;
}

// getting 方法
@Override // 实现自 EnvironmentCapable 接口
public ConfigurableEnvironment getEnvironment() {
    // 如果 environment 为空,主动创建
	if (this.environment == null) {
		this.environment = createEnvironment();
	}
	return this.environment;
}

protected ConfigurableEnvironment createEnvironment() {
	return new StandardServletEnvironment();
}
//必须要配置的属性的集合
private final Set<String> requiredProperties = new HashSet<>(4);
	@Override
	//final修饰,是不允许子类重写的
	public final void init() throws ServletException {
		if (logger.isDebugEnabled()) {
			logger.debug("Initializing servlet '" + getServletName() + "'");
		}
		//目的是解析<init-param>标签,将其内容封装到PropertyValues pvs中
		PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
		if (!pvs.isEmpty()) {
			try {
			//将当前的Servlet类封装成BeanWrapper对象
				BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
				ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
				//注册自定义属性编辑器,遇到了Resource属性就会用ResourceLoader进行解析
				bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
				//空实现
				initBeanWrapper(bw);
				//以spring方式将pvs注入到bw中
				bw.setPropertyValues(pvs, true);
			}
			catch (BeansException ex) {
				if (logger.isErrorEnabled()) {
					logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
				}
				throw ex;
			}
		}
		//子类实现
		initServletBean();
		if (logger.isDebugEnabled()) {
			logger.debug("Servlet '" + getServletName() + "' configured successfully");
		}
	}

下面分析一下上面的几个代码:
1️⃣ServletConfigPropertyValues
这是HttpServletBean的私有静态类,是ServletConfig的PropertyValues封装实现类。

private static class ServletConfigPropertyValues extends MutablePropertyValues {
		public ServletConfigPropertyValues(ServletConfig config, Set<String> requiredProperties)
				throws ServletException {
				//获取一个缺失属性的集合
			Set<String> missingProps = (!CollectionUtils.isEmpty(requiredProperties) ?
					new HashSet<>(requiredProperties) : null);
			//遍历ServletConfig的初始化参数集合,添加到 ServletConfigPropertyValues 中,并从 missingProps 移除
			Enumeration<String> paramNames = config.getInitParameterNames();
			while (paramNames.hasMoreElements()) {
				String property = paramNames.nextElement();
				Object value = config.getInitParameter(property);
				//加入MutablePropertyValues的propertyValueList中
				addPropertyValue(new PropertyValue(property, value));
				if (missingProps != null) {
					missingProps.remove(property);
				}
			}
			//如果传入的参数中没有必须要设置的参数就抛出错误
			if (!CollectionUtils.isEmpty(missingProps)) {
				throw new ServletException(
						"Initialization from ServletConfig for servlet '" + config.getServletName() +
						"' failed; the following required properties were missing: " +
						StringUtils.collectionToDelimitedString(missingProps, ", "));
			}
		}
	}

2️⃣PropertyAccessorFactory.forBeanPropertyAccess(this)
将目标对象包装成BeanWrapperImpl,BeanWrapper 是 Spring 提供的一个用来操作 Java Bean 属性的工具,使用它可以直接修改一个对象的属性。
3️⃣bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
注册自定义的属性编辑器
4️⃣bw.setPropertyValues(pvs, true);
将pvs注入BeanWrapper对象中,例如可以将配置注入到FrameworkServlet的成员变量中。
5️⃣initServletBean()
这个方法是在子类FrameworkServlet中实现
首先先看看FrameworkServlet中的属性:
//创建的容器类型
private Class<?> contextClass = DEFAULT_CONTEXT_CLASS;
//配置文件的位置
private String contextConfigLocation;
//context对象
private WebApplicationContext webApplicationContext;
有四种方式来创建:
1️⃣public FrameworkServlet(WebApplicationContext webApplicationContext)构造函数直接传入
2️⃣public void setApplicationContext(ApplicationContext applicationContext)通过spring注入
3️⃣findWebApplicationContext()
4️⃣createWebApplicationContext(WebApplicationContext parent)

@Override
	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 {
		//初始化容器
			this.webApplicationContext = initWebApplicationContext();
			//空实现
			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");
		}
	}

接下来看看initWebApplicationContext()

protected WebApplicationContext initWebApplicationContext() {
//首先获得根容器,就是注册的rootWebApplication
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;
//如果已经通过构造函数传入,那么就可以直接使用
		if (this.webApplicationContext != null) {
			// A context instance was injected at construction time -> use it
			wac = this.webApplicationContext;
			 如果是 ConfigurableWebApplicationContext 类型,并且未激活,则进行初始化
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {
					// The context has not yet been refreshed -> provide services such as
					// setting the parent context, setting the application context id, etc
					if (cwac.getParent() == null) {
						// The context instance was injected without an explicit parent -> set
						// the root application context (if any; may be null) as the parent
						cwac.setParent(rootContext);
					}
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		//第二种情况,从 ServletContext 获取对应的 WebApplicationContext 对象
		if (wac == null) {
			wac = findWebApplicationContext();
		}
		//创建一个context容器
		if (wac == null) {
			// No context instance is defined for this servlet -> create a local one
			wac = createWebApplicationContext(rootContext);
		}
		//如果未触发刷新事件,则主动触发刷新事件
		if (!this.refreshEventReceived) {
			onRefresh(wac);
		}
//将 context 设置到 ServletContext 中
		if (this.publishContext) {
			// Publish the context as a servlet context attribute.
			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;
	}

接下来是createWebApplicationContext的实现:

protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
		Class<?> contextClass = getContextClass();
		if (this.logger.isDebugEnabled()) {
			this.logger.debug("Servlet with name '" + 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 '" + getServletName() +
					"': custom WebApplicationContext class [" + contextClass.getName() +
					"] is not of type ConfigurableWebApplicationContext");
		}
		//创建context类的对象
		ConfigurableWebApplicationContext wac =
				(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
		//设置 environment、parent、configLocation 属性
		wac.setEnvironment(getEnvironment());
		wac.setParent(parent);
		String configLocation = getContextConfigLocation();
		if (configLocation != null) {
			wac.setConfigLocation(configLocation);
		}
		//初始化wac
		configureAndRefreshWebApplicationContext(wac);

		return wac;
	}

初始化wac的方法:

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
//同样的如果是默认编号就重新设置id属性
		if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
			// The application context id is still set to its original default value
			// -> assign a more useful id based on available information
			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 的 servletContext、servletConfig、namespace 属性
		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 ,从而初始化 wac
		wac.refresh();
	}

真正的在onRefresh中实现了SpringMVC组件的初始化,而该方法的具体实现是在DispatcherServlet中:

	@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);
	}

而这个方法将会以两种方式被触发:
1️⃣就是initWebApplicationContext中

		if (!this.refreshEventReceived) {
			onRefresh(wac);
		}

2️⃣在上述的SourceFilteringListener中回调
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
在这里插入图片描述
原始类
private final Object source;
代理的监听器
private GenericApplicationListener delegate;
核心方法:

protected void onApplicationEventInternal(ApplicationEvent event) {
		if (this.delegate == null) {
			throw new IllegalStateException(
					"Must specify a delegate object or override the onApplicationEventInternal method");
		}
		this.delegate.onApplicationEvent(event);
	}

将事件转发给delegate监听器。
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));传入的参数中
还有ContextRefreshListener对象,这是FrameworkServlet的内部类:

	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(event.getApplicationContext());
	}

OK,完结,最终发现监听后会执行onRefresh方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值