springMVC 初始化
继承关系
DispatcherServlet > FrameworkServlet > HttpServletBean > HttpServlet > GenericServlet > Servlet
初始化流程
-
DispatcherServlet 是一个 Servlet,所有的 Servlet 初始化都会执行 init 方法(JAVA EE 的知识,别忘了)
-
HttpServletBean 复写了
init()
方法(子类复写父类方法,会调用子类的方法,如果子类没有复写,就调用父类自己的,面向对象知识别忘了) -
HttpServletBean 在
init()
中,又调用了initServletBean()
方法,这又是个模板方法,于是又找子类 FrameworkServlet,入口就是这里,开始分析 -
org.springframework.web.servlet.FrameworkServlet#initServletBean
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; } .... }
-
org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext
protected WebApplicationContext initWebApplicationContext() { // 获取 spring 的 IOC 容器(什么时候创建的?) WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; // 如果 xml 方式,一开始 springMVC 的 IOC 容器肯定是空的,会走后面的 if 去创建 // 但如果是注解方式,webApplicationContext 就不为空(什么时候创建的?) if (this.webApplicationContext != null) { wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { if (cwac.getParent() == null) { // 把 spring 的容器设置为 spirngMVC 容器的父容器 cwac.setParent(rootContext); } // 配置和刷新 springMVC 的容器 // 这里面先调用 refresh() 方法(刷新),然后调用 finishRefresh() 方法(完成刷新) // 重点看下 finishRefresh 方法 configureAndRefreshWebApplicationContext(cwac); } } } ... return wac; }
问题一:为什么刚开始 spring 和 springMVC 的容器就有了?
Sevlet 3.0 的新特性,ServletContainerInitializer 接口的 onStartup 方法能在 Servlet 启动时注册 Filter、Listener 等组件,srping 根据这个特性使用 SpringServletContainerInitializer 这个类完成 spring 和 springMVC 容器的创建
问题二:为什么要把 spring 的容器作为 springMVC 的父容器?
在 springMVC 的 IOC 容器中获取不到 bean 时,可以到父容器( spring 的 IOC 容器)中获取。有没有觉得 spring 和 springMVC 无缝衔接,简直天作之合。反之 spring 容器是获取不到 springMVC 中的 bean 的(这曾经是一道牛逼的面试题)
-
org.springframework.context.support.AbstractApplicationContext#finishRefresh
protected void finishRefresh() { this.clearResourceCaches(); this.initLifecycleProcessor(); this.getLifecycleProcessor().onRefresh(); // 主要是这里,完成 springMVC 容器刷新后,发布了一个事件,监听这个事件的监听器就会执行 this.publishEvent((ApplicationEvent)(new ContextRefreshedEvent(this))); LiveBeansView.registerApplicationContext(this); }
spring 事件机制:事件,事件发布者,事件监听器。spring 通过观察者设计模式实现 当事件发布时,对应的事件监听器会执行
-
org.springframework.web.servlet.FrameworkServlet.ContextRefreshListener#onApplicationEvent
// FrameworkServlet 的子类 ContextRefreshListener 在监听这个事件 private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEvent(ContextRefreshedEvent event) { FrameworkServlet.this.onApplicationEvent(event); } }
-
onApplicationEvent 方法调用自己的 onRefresh 方法,这又是个模板方法,最后会调用到 DispatcherServlet 的 initStrategies 方法
protected void initStrategies(ApplicationContext context) { // 刚好九个,这是在初始化 DispatcherServlet 就打组件 initMultipartResolver(context); // 文件上传解析器 initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); // 处理器映射器 initHandlerAdapters(context); // 处理器适配器 initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); }
-
看看处理器映射器的初始化
initHandlerMappings
private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; // detectAllHandlerMappings 这个变量在类中定义的时候,初始化值就是 true if (this.detectAllHandlerMappings) { // 这里能获取到已经加载的所有处理器映射器,怎么来的?MVC 注解驱动,注解@EnableWebMvc 或 xml <mvc:annotation-drive /> Map<String, HandlerMapping> 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 { // 因为变量未 true 这里不会走 } // 如果没有处理器映射器,就会创建默认的,默认的在一个配置文件中,DispatcherServlet 同级目录下 DispatcherServlet.properties if (this.handlerMappings == null) { this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isTraceEnabled()) { logger.trace("No HandlerMappings declared for servlet '" + getServletName() + "': using default strategies from DispatcherServlet.properties"); } } }
HandlerMapping 是个重要的组件,RequestMappingHandlerMapping 这个处理器映射器专门用来处理 @RequestMapping 标注的 handler
里面有很多重要属性,比如 mappingRegistry、lookup 等,也有很多重要方法,下面贴两个图看下 大致看下 url 和 处理器映射器的关系
RequestMappingHandlerMapping 里面有什么
HttpServletRequest 里面有什么
-
处理器适配器和处理器映射器大致一致,需要注意的是适配器初始化时会维护好参数和响应解析器,后面文章分析请求处理流程时具体说解析器