springMVC 初始化

继承关系

DispatcherServlet > FrameworkServlet > HttpServletBean > HttpServlet > GenericServlet > Servlet

初始化流程

  1. DispatcherServlet 是一个 Servlet,所有的 Servlet 初始化都会执行 init 方法(JAVA EE 的知识,别忘了)

  2. HttpServletBean 复写了 init() 方法(子类复写父类方法,会调用子类的方法,如果子类没有复写,就调用父类自己的,面向对象知识别忘了)

  3. HttpServletBean 在init() 中,又调用了 initServletBean() 方法,这又是个模板方法,于是又找子类 FrameworkServlet,入口就是这里,开始分析

  4. 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;
    		}
    		....
    	}
    
  5. 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 的(这曾经是一道牛逼的面试题)

  6. 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 通过观察者设计模式实现 当事件发布时,对应的事件监听器会执行

  7. 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);
        }
    }
    
  8. 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);
    }
    
  9. 看看处理器映射器的初始化 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 里面有什么

  10. 处理器适配器和处理器映射器大致一致,需要注意的是适配器初始化时会维护好参数和响应解析器,后面文章分析请求处理流程时具体说解析器

posted @ 2024-07-01 15:53  CyrusHuang  阅读(1)  评论(0编辑  收藏  举报