Spring MVC源码解析
在讲解springmvc之前-其实是需要大家了解一点tomcat的源码知识的-但是大部分的初学者还只停留在应用的层面-所以-下面展示tomcat容器初始化的流程图和加载servlet的流程图-大家只需要先记住他们的执行顺序即可-等后续开始tomcat源码之后我们再做下一步深入了解。
1、Tomcat容器初始化流程图
!img
2、tomcat加载servlet流程图
!img
从上述流程开始看起-我们发现最终会调用Servlet的init方法-SpringMVC中最核心的类就是DispatcherServlet-因此需要找到init方法。
1、DispatcherServlet的初始化
DispatcherServlet的类图:
可以看到,DispatcherServlet继承自HttpServlet-它的本质就是一个Servlet-但是此类中并没有init方法-因此要去父类中进行查找-最终在HttpServletBean类中重写了父类GenericServlet的init方法。因此当tomcat容器启动的时候会调用init方法开始执行-中间会经历N多个环节-此处不需要了解-唯一需要注意的一个点-就在于SpringMVC的组件会调用DispatcherServlet的组件进行初始化工作-这些初始化工作会完成对于九大组件的初始化-这个初始化会从DispatcherServlet.properties文件中进行相应的属性值加载。
HttpServletBean———init()
``java
public final void init() throws ServletException { // Set bean properties from init parameters. // 将web.xml文件中初始化参数设置到bean中-requiredProperties为必须参数 PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); if (!pvs.isEmpty()) { try { //将DispatcherServlet类添加到BeanWrapper的包装类中 BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); //对DispatcherServlet进行初始化工作 initBeanWrapper(bw); //将配置的初始化值设置到DispatcherServlet中 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(); }
`
调用子类方法实现初始化BeanServlet
FrameworlServlet——initServletBean
`java
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 { // webApplicationContext是FrameworkServlet的上下文-后续的方法是进行上下万的初始化 this.webApplicationContext = initWebApplicationContext(); // 初始化FrameworkServlet-默认实现为null-由子类进行实现 initFrameworkServlet(); } catch (ServletException | RuntimeException ex) { logger.error("Context initialization failed", ex); throw ex; } if (logger.isDebugEnabled()) { String value = this.enableLoggingRequestDetails ? "shown which may lead to unsafe logging of potentially sensitive data" : "masked to prevent unsafe logging of potentially sensitive data"; logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails + "': request parameters and headers will be " + value); } if (logger.isInfoEnabled()) { logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms"); } }
`
此后的流程会进入到Spring的onRefresh方法中-最终会调用DispatcherServlet中的onRefresh方法。
`java
@Override protected void onRefresh(ApplicationContext context) { initStrategies(context); } /** * Initialize the strategy objects that this servlet uses. *
`
这几个组件的初始化过程都差不多-因此我们选择一个来重点描述-其他的需要大家下去之后自己来研究了。
`java
private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; // 是否查找所有HandlerMapping标识 if (this.detectAllHandlerMappings) { // Find all HandlerMappings in the ApplicationContext, including ancestor contexts. // 从上下文中查找HandlerMapping类型的Bean Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<>(matchingBeans.values()); // We keep HandlerMappings in sorted order. AnnotationAwareOrderComparator.sort(this.handlerMappings); } } else { try { // 根据指定名称获取HandlerMapping对象 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. } } // Ensure we have at least one HandlerMapping, by registering // a default HandlerMapping if no other mappings are found. // 确保至少有一个HandlerMapping-如果没有找到-使用默认策略-注册一个默认的 if (this.handlerMappings == null) { this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isTraceEnabled()) { logger.trace("No HandlerMappings declared for servlet '" + getServletName() +