springboot mvc自动配置(三)初始化mvc的组件
所有文章
https://www.cnblogs.com/lay2017/p/11775787.html
正文
在springboot mvc自动配置的时候,获得了DispatcherServlet和DispatcherServletRegistrationBean。DispatcherServletRegistrationBean将DispatcherServlet给注册到了ServletContext当中。
注册到ServletContext中的Servlet将会触发其init方法,从而进行Servlet的初始化。本文将从Servlet的init方法开始,看看触发MVC各个组件初始化的代码
DispatcherServlet类图
我们先看看DispatcherServlet的类图
根据类图可以看到两部分的设计,第一部分是Servlet到HttpServlet,也就是Servlet容器相关的内部设计。第二部分是Spring在HttpServlet的基础上扩展了框架相关的内容,而最终DispatcherServlet将扩展springMVC的内容。
GenericServlet
我们跟进GenericServlet的init方法,看看它的实现
@Override public void init(ServletConfig config) throws ServletException { this.config = config; this.init(); }
继续跟进init方法
public void init() throws ServletException { // NOOP by default }
没有实现逻辑,供子类去选择实现
HttpServletBean
httpServlet么有实现init方法,由HttpServletBean这个spring实现的类来扩展。跟进HttpServletBean的init方法
@Override public final void init() throws ServletException { // Set bean properties from init parameters. PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); if (!pvs.isEmpty()) { try { 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) { if (logger.isErrorEnabled()) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); } throw ex; } } // Let subclasses do whatever initialization they like. initServletBean(); }
HttpServletBean也是扩展了一个initServletBean接口来供子类实现
FrameworkServlet
跟进FrameworkServlet的initServletBean的方法
@Override protected final void initServletBean() throws ServletException { getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'"); if (logger.isInfoEnabled()) { logger.info("Initializing Servlet '" + getServletName() + "'"); } long startTime = System.currentTimeMillis(); try { this.webApplicationContext = initWebApplicationContext(); initFrameworkServlet(); } catch (ServletException | RuntimeException ex) { logger.error("Context initialization failed", ex); throw ex; } // ... }
initFrameworkServlet是一个空实现,核心逻辑落到了initWebApplicationContext中,跟进它
protected WebApplicationContext initWebApplicationContext() { WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { //... } if (wac == null) { wac = findWebApplicationContext(); } if (wac == null) { wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { synchronized (this.onRefreshMonitor) { onRefresh(wac); } } if (this.publishContext) { String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); } return wac; }
Springboot在启动过程中创建了ApplicationContext,这里将公用同一个ApplicationContext。
onRefresh方法提供了一个空实现,供子类去做初始化实现
protected void onRefresh(ApplicationContext context) { // For subclasses: do nothing by default. }
DispatcherServlet
跟进DispatcherServlet的onRefresh方法
@Override 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); }
我们看到,initStrategies包含了各种MVC组件的初始化方法
组件初始化
我们打开initMultipartResolver看看
private void initMultipartResolver(ApplicationContext context) { try { this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class); // ... catch (NoSuchBeanDefinitionException ex) { // Default is no multipart resolver. this.multipartResolver = null; // ... } }
其实就是从Bean工厂当中获取对应的Bean对象。multipartResolver默认可能为空
再打开initViewResolvers看看
private void initViewResolvers(ApplicationContext context) { this.viewResolvers = null; if (this.detectAllViewResolvers) { Map<String, ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false); if (!matchingBeans.isEmpty()) { this.viewResolvers = new ArrayList<>(matchingBeans.values()); AnnotationAwareOrderComparator.sort(this.viewResolvers); } } else { try { ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class); this.viewResolvers = Collections.singletonList(vr); } catch (NoSuchBeanDefinitionException ex) { // Ignore, we'll add a default ViewResolver later. } } if (this.viewResolvers == null) { this.viewResolvers = getDefaultStrategies(context, ViewResolver.class); } }
一样的是从Bean工厂当中获取Bean,只不过这里是获取到一个集合,且如果没有将会获取默认的策略。
其它的MVC组件初始化类似,这里不每个打开看了。
总结
DispatcherServlet作为一个Servlet的实现,在Servlet被调用init方法以后最终将会调用DispatcherServlet的initStrategies方法,该方法将会初始化各个组件。
初始化组件基本就是把各个Bean对象从BeanFactory中拿出来组合到DispatcherServlet中,供后续使用。