Spring IoC是Spring的核心,所以IoC也是Spring MVC的基础,Spring MVC 会建立起一个IOC容器体系,IoC的启动过程与web容器启动的过程是集成在一起的,它们的启动由ContextLoaderListener监听器负责完成。建立IOC容器体系后,DispatcherServlet作为Spring MVC处理web请求的转发器也建立起来,从而完成响应HTTP请求的准备工作。
IOC容器的启动(ContextLoaderListener)
IoC容器的启动过程就是建立上下文的过程,由ContextLoaderListener启动的上下文为根上下文,在根上下文的基础上,还有一个与Web MVC相关的上下文作为根上下文的子上下文来保存DispatcherServlet所需要的MVC对象,构成一个上下文体系。这个上下文体系的建立与初始化是由ContextLoader来完成的。
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public ContextLoaderListener() {
}
public ContextLoaderListener(WebApplicationContext context) {
super(context);
}
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
@Override
public void contextDestroyed(ServletContextEvent event) {
closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}
在ContextLoaderListener中实现了ServletContextListener接口,这个接口里的函数会结合Web容器的生命周期被调用(contextInitialized,服务器启动时被调用;contextDestroyed,服务器关闭时被调用)。因为ServletContextListener是ServletContext的监听者,如果ServletContext发生变化,会触发预先设计好的动作,比如初始化和销毁。比如初始化:
ContextLoaderListener.contextInitialized
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
//初始化WebApplicationContext根上下文
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
//根上下文作为唯一实例存在,用ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE标志位来实现
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!");
}
Log logger = LogFactory.getLog(ContextLoader.class);
servletContext.log("Initializing Spring root WebApplicationContext");
if (logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started");
}
long startTime = System.currentTimeMillis();
try {
if (this.context == null) {
//创建根上下文
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
//转型为需要产生的IoC容器
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
// 载入根上下文的双亲上下文
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
//设置ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE标志位
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
}
else if (ccl != null) {
currentContextPerThread.put(ccl, this.context);
}
if (logger.isDebugEnabled()) {
logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
}
if (logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
}
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;
}
}
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
//判断使用什么样的类作为Web容器中的IoC容器
Class<?> contextClass = determineContextClass(sc);
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
}
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}
protected Class<?> determineContextClass(ServletContext servletContext) {
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
//如果在ServletContext中配置了CONTEXT_CLASS_PARAM,则使用这个class反射根上下文。
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 {//如果没有配置CONTEXT_CLASS_PARAM,则使用默认的contextClass来反射根上下文
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);
}
}
}
//设置ServletContext以及配置各类参数,最终调用refresh方法来初始化容器
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
if (idParam != null) {
wac.setId(idParam);
}
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
wac.setServletContext(sc);
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
}
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
}
customizeContext(sc, wac);
wac.refresh();
}
ContextLoaderListener初始化根上下文过程图
Spring MVC的启动(DispatcherServlet)
在完成ContextLoaderListener的初始化后,Web容器开始初始化DispatcherServlet。DispatcherServlet会建立自己的上下文来持有Spring MVC的Bean对象,在建立自己持有的IOC容器时,会将根上下文作为自己持有的上下文的双亲上下文,建立了这个上下文后将该上下文保存到ServletContext中,供以后检索和使用。DispatcherServlet的启动过程也就是Spring MVC的启动过程。
DispatcherServlet的继承关系
DispatcherServlet通过继承FrameworkServlet和HttpServletBean来继承HttpServlet,这样就可以使用Servlet API来对HTTP请求进行响应,成为Spring MVC的前端处理器。
DispatcherServlet的工作分为两部分:一个是初始化部分,通过调用initStrategies方法完成Spring MVC的初始化;一个是对HTTP请求进行响应,作为一个servlet,Web容器会调用Servlet的doGet和doPost方法来完成响应动作。
DispatcherServlet的启动与初始化
DispatcherServlet的启动与Servlet的启动是相似的,在Servlet初始化时会调用init方法,在DispatcherServlet的基类HttpServletBean的初始化也由init开始。DispatcherServlet持有一个以自己的Servlet名称命名的IoC容器,这个容器作为根上下文的子上下文存在,在Web容器的上下文体系中,一个根上下文可以作为多个Servlet子上下文的双亲上下文。
一般情况下controller相关的bean或者对应的注解扫描配置在子上下文中,而service、dao的bean或者对应的注解扫描配置在根上下文中。
建立MVC的上下文代码逻辑:
HttpServletBean.init()
@Override
public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}
// 获取Servlet的初始化参数,配置Bean属性
try {
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(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");
}
}
@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");
}
}
//这里的初始化上下文和ContextLoaderListner不太一样
protected WebApplicationContext initWebApplicationContext() {
//使用ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE来获取根上下文
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
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) {
wac = findWebApplicationContext();
}
//如果以上都没有能获取MVC的IoC容器,则创建一个
if (wac == null) {
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
onRefresh(wac);
}
if (this.publishContext) {
//把建立好的子上下文存放到ServletContext中去,使用的属性名就是当前servlet名
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;
}
protected WebApplicationContext createWebApplicationContext(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");
}
//实例化需要的具体上下文对象,这个上下文对象也是XmlWebApplicationContext
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 {
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);
//通过refresh方法来调用容器初始化过程
wac.refresh();
}
DispatcherServlet.onRefresh
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
//启动整个Spring MVC框架的初始化
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
MVC框架的初始化是在initStrategies方法中完成的,初始化了支持国际化的LocaleResolver、request映射的HandlerMappings、视图生成的ViewResolvers等。
//初始化HandlerMapping
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
//detectAllHandlerMappings这里默认为true,即默认从所有的IoC容器中获取HandlerMapping
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {//否则从当前的IoC容器中通过getBean获取所有Bean名称为“handlerMapping”的HandlerMapping
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// 如果没有handlerMappings,则从配置文件中读取
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isDebugEnabled()) {
logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
}
}
}
initHandlerMappings是对HandlerMappings的初始化,这些Map的作用是为HTTP请求找到相应的Controller控制器,以完成功能逻辑。IoC容器初始化后,容器里有各种各样的Bean,initHandlerMappings会从IoC容器中通过getBean来获取handlerMapping,handlerMapping包含了MVC中Controller的定义和配置,同时handlerMappings变量就已经获取了在BeanDefinition配置好的映射关系。加载的HandlerMapping被放在一个List中并已经排好序。
initHandlerMappings方法调用栈
参考文献:
《Spring技术内幕 深入解析Spring架构与设计原理》
热门评论
恩,感觉写的不错,哎太菜了没怎么看很懂 懵懂,
上下文说的太官方了,不是很懂