在上一篇文章中,我们详解了spring的启动过程,这一篇介绍spring mvc的启动过程,那么spring和spring mvc有什么联系呢。
1.Spring和SpringMVC是父子容器关系。
2.Spring整体框架的核心思想是容器,用来管理bean的生命周期,而一个项目中
会包含很多容器,并且它们分上下层关系,目前最常用的一个场景是在一个项目
中导入Spring和SpringMVC框架,而Spring和SpringMVC其实就是两个容器,
Spring是父容器,SpringMVC是子容器,Spring父容器中注册的Bean对
SpringMVC子容器是可见的,反之则不行。
3.按照官方文档推荐,根据不同的业务模块来划分不同的容器中注册不同的Bean,
SpringMVC主要就是为我们构建web应用程序,那么SpringMVC子容器用来注册
web组件的Bean,如控制器(controller层,这也是为什么spring配置文件中包扫描
排除controller的原因)、处理器映射、视图解析器等。而Spring用来注册其他Bean,
这些Bean通常是驱动应用后端的中间层和数据层组件。
而在spring boot以前,我们常用的架构是spring+spring mvc,上文也提起过,web项目的启动过程是先读web.xml文件,web.xml 加载顺序为: context-param < listener < filter < servlet。
在上篇文章中spring root容器的初始化是在contextLoaderListenner这个上下文监听器中实现的,再往下走spring mvc的启动是在DispatcherServlet中。
springmvc的核心是DispatcherServlet,它是请求调度控制器,负责拦截客户端请求,根据url通过HandlerMapping找到对应的handler,通过HandlerAdapter根据handler匹配对应的controller,完成接口调用后返回model,然后通过ViewResolver渲染视图,最后DispatcherServlet响应用户请求。
继承关系: DispatcherServlet -> FrameworkServlet -> HttpServletBean
初始化
1.HttpServletBean 的 init() 方法
@Override
public final void init() throws ServletException {
try {
// ServletConfigPropertyValues是静态内部类,使用ServletConfig获取 web.xml中配置的参数
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
// 使用 BeanWrapper 来构造 DispatcherServlet
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) {}
// 让子类实现的方法,这种在父类定义在子类实现的方式叫做模版方法模式
initServletBean();
}
2.FrameworkServlet 的 initServletBean() 方法
protected final void initServletBean() throws ServletException {
this.getServletContext().log("Initializing Spring " + this.getClass().getSimpleName() + " '" + this.getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("Initializing Servlet '" + this.getServletName() + "'");
}
long startTime = System.currentTimeMillis();
try {
//初始化 WebApplicationContext (即SpringMVC的IOC容器)
this.webApplicationContext = this.initWebApplicationContext();
this.initFrameworkServlet();
} catch (RuntimeException | ServletException var4) {
this.logger.error("Context initialization failed", var4);
throw var4;
}
......错误日志略......
}
点进 this.initWebApplicationContext();
protected WebApplicationContext initWebApplicationContext() {
// 获取 ContextLoaderListener 初始化并注册在 ServletContext 中的根容器,即 Spring 的容器
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// 因为 WebApplicationContext 不为空,说明该类在构造时已经将其注入
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)wac;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
// 将 Spring 的容器设为 SpringMVC 容器的父容器
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// 如果 WebApplicationContext 为空,则进行查找,能找到说明上下文已经在别处初始化。
wac = findWebApplicationContext();
}
if (wac == null) {
// 如果 WebApplicationContext 仍为空,则以 Spring 的容器为父上下文建立一个新的。
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// 重点方法,由 DispatcherServlet 实现,后续会讲解
onRefresh(wac);
}
if (this.publishContext) {
// 发布这个 WebApplicationContext 容器到 ServletContext 中
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
点进createWebApplicationContext(rootContext);
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
Class<?> contextClass = this.getContextClass();
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Fatal initialization error in servlet with name '" + this.getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext");
} else {
ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(this.getEnvironment());
wac.setParent(parent);
String configLocation = this.getContextConfigLocation();
if (configLocation != null) {
wac.setConfigLocation(configLocation);
}
//重要方法
this.configureAndRefreshWebApplicationContext(wac);
return wac;
}
}
点进this.configureAndRefreshWebApplicationContext(wac);
其实在这里已经和spring的启动源码一样了
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(this.getServletContext().getContextPath()) + '/' + this.getServletName());
}
}
wac.setServletContext(this.getServletContext());
wac.setServletConfig(this.getServletConfig());
wac.setNamespace(this.getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new FrameworkServlet.ContextRefreshListener()));
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment)env).initPropertySources(this.getServletContext(), this.getServletConfig());
}
this.postProcessWebApplicationContext(wac);
this.applyInitializers(wac);
//点进
wac.refresh();
}
wac.refresh();这个方法在上文已经介绍过了,所以spring和spring mvc容器的初始化过程是一样的。
public void refresh() throws BeansException, IllegalStateException {
Object var1 = this.startupShutdownMonitor;
synchronized(this.startupShutdownMonitor) {
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory);
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}
}
}
spring mvc容器初始化完成,里面的bean也初始化完成。
具体是在 this.finishBeanFactoryInitialization(beanFactory);中完成的,
在上一篇文章中我并没有详细介绍这个方法,下面贴张图
这两张图是普通的bean实例化,以及requestmapping注解的方法实例化成RequestMappingHandlerMapping 实例。
bean的实例化无非就是从BeanDefinition中获取bean的信息,执行一系列的BeanPostProcessor,最后通过构造器反射创建bean,注入bean的属性等等
接下来看DispatcherServlet 实现的onRefresh(wac);
protected void onRefresh(ApplicationContext context) {
this.initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
this.initMultipartResolver(context);
this.initLocaleResolver(context);
this.initThemeResolver(context);
//重要方法,可自己了解
this.initHandlerMappings(context);
this.initHandlerAdapters(context);
this.initHandlerExceptionResolvers(context);
this.initRequestToViewNameTranslator(context);
this.initViewResolvers(context);
this.initFlashMapManager(context);
}
initHandlerMappings() 方法从 SpringMVC 的容器及 Spring 的容器中查找所有的 HandlerMapping 实例,并把它们放入到 handlerMappings 这个 list 中。这个方法并不是对 HandlerMapping 实例的创建,HandlerMapping 实例是在上面 WebApplicationContext 容器初始化,即 SpringMVC 容器初始化的时候创建的。
我们通常在页面发起一个request请求,springMVC会先解析url,然后通过url去urlLookup中找对应的mapping,再根据mapping去mappingLookup中找Controller的Mapping处理方法HandlerMethod,如果有cros配置,那么最后再根据HandlerMethod找到对应的配置,最后通过反射最终调用到我们Controller的@RequestMapping方法。