美文网首页
DispatcherServlet初始化

DispatcherServlet初始化

作者: aix91 | 来源:发表于2019-03-13 13:51 被阅读0次

    1. 作用

    DispatcherServlet主要负责流程的控制

    • 文件上传解析
    • 通过handlerMapping将请求映射处理器
    • 通过HandlerAdapter支持多种类型的处理器
    • 通过ViewResolver解析逻辑视图名到具体视图
    • 本地化解析
    • 渲染具体视图
    • HandlerExceptionResolver解析异常

    2. DispatcherServlet context初始化

    • init(): 在servlet初始化阶段,会调用起init()方法,该方法在其父类HttpServletBean中
    public final void init() throws ServletException {
            if (logger.isDebugEnabled()) {
                logger.debug("Initializing servlet '" + getServletName() + "'");
            }
    
            // Set bean properties from init parameters.
            try {
                //解析init-param
                PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
                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) {
                logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
                throw ex;
            }
    
            // Let subclasses do whatever initialization they like.
            initServletBean();
    
            if (logger.isDebugEnabled()) {
                logger.debug("Servlet '" + getServletName() + "' configured successfully");
            }
        }
    
    
    • initServletBean, 在DispatcherServlet父类FrameworkServlet中找到了该方法:
    protected final void initServletBean() throws ServletException {
            ...
        this.webApplicationContext = initWebApplicationContext();
        initFrameworkServlet();
            ...
    } 
    
    • initWebApplicationContext
      ContextLoaderListener初始化的Context(代码中的rootContext)是对于整个应用程序共享的,不管是使用什么表现层技术,一般如DAO层、Service层Bean;
      DispatcherServlet初始化的context(wac)是只对Spring Web MVC有效的Bean,如Controller、HandlerMapping、HandlerAdapter等等...
    protected WebApplicationContext initWebApplicationContext() {
            WebApplicationContext rootContext =
        WebApplicationContextUtils.getWebApplicationContext(getServletContext());
            WebApplicationContext wac = null;
            if (this.webApplicationContext != null) {
                wac = this.webApplicationContext;
                if (wac instanceof ConfigurableWebApplicationContext) {
                    ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
                    if (!cwac.isActive()) {
                        if (cwac.getParent() == null) {
                            cwac.setParent(rootContext);
                        }
                        configureAndRefreshWebApplicationContext(cwac);
                    }
                }
            }
            if (wac == null) {
                wac = findWebApplicationContext();
            }
            if (wac == null) {
                wac = createWebApplicationContext(rootContext);
            }
            if (!this.refreshEventReceived) {
                onRefresh(wac);
            }
            if (this.publishContext) {
                // Publish the context as a servlet context attribute.
                String attrName = getServletContextAttributeName();
                getServletContext().setAttribute(attrName, wac);
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
                            "' as ServletContext attribute with name [" + attrName + "]");
                }
            }
    
            return wac;
        }
    

    这里提供了三种方式创建DispatcherServlet的context:

    • this.webApplicationContext != null:通过构造函数创建(怎么创建的,还有待研究)
    • 通过contextAttribute进行初始化
    • 重新创建WebApplicationContext
      在初始化ContextBean的最后,会调用configureAndRefreshWebApplicationContext刷新,初始化容器,最终会调用AbstractApplicationContext类的refresh方法:Spring注解--容器创建源码分析(一):整体流程
      在容器初始化的最后,会调用finishRefresh方法
    protected void finishRefresh() {
            // Initialize lifecycle processor for this context.
            initLifecycleProcessor();
    
            // Propagate refresh to lifecycle processor first.
            getLifecycleProcessor().onRefresh();
    
            // Publish the final event.
            publishEvent(new ContextRefreshedEvent(this));
    
            // Participate in LiveBeansView MBean, if active.
            LiveBeansView.registerApplicationContext(this);
        }
    

    这个方法中的publishEvent会触发ApplicationListener事件。来到FrameWorkServlet类中,会看到一个implement ApplicationListener的内部类

        private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
    
            @Override
            public void onApplicationEvent(ContextRefreshedEvent event) {
                FrameworkServlet.this.onApplicationEvent(event);
            }
        }
    

    DispatcherServlet的其他组件就是从这里开始完成初始化的。

    3. DispatcherServlet其他组件的初始化

    从上面debug来到DispatcherServlet的initStrategies方法

        protected void initStrategies(ApplicationContext context) {
            initMultipartResolver(context);
            initLocaleResolver(context);
            initThemeResolver(context);
            initHandlerMappings(context);
            initHandlerAdapters(context);
            initHandlerExceptionResolvers(context);
            initRequestToViewNameTranslator(context);
            initViewResolvers(context);
            initFlashMapManager(context);
        }
    
    • handlerMapping初始化
      默认情况下(detectAllHandlerMappings=true)会加载所有实现了HandlerMapping接口的bean。可以在web.xml中修改参数
    <init-param>
        <param-name>detectAllHandlerMappings</param-name>
        <param-value>false</param-value>
    </init-param>
    

    这样,就只会加载名为"handlerMapping"的HandlerMapping。如果在Context中没有找到HandlerMapping, SpringMVC会去DispatcherServlet.properties文件中找到 org.springframework.web.servlet.HandlerMapping 相关类名加载

    ...
    org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
        org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
    
    org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
        org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
        org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
    
    ...
    
    
    private void initHandlerMappings(ApplicationContext context) {
            this.handlerMappings = null;
    
            if (this.detectAllHandlerMappings) {
                Map<String, HandlerMapping> matchingBeans =
                        BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
                if (!matchingBeans.isEmpty()) {
                    this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
                    // We keep HandlerMappings in sorted order.
                AnnotationAwareOrderComparator.sort(this.handlerMappings);
                }
            }
            else {
                try {
                    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.
            if (this.handlerMappings == null) {
                this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
                if (logger.isDebugEnabled()) {
                    logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
                }
            }
        }
    
    • 初始化HandlerAdapters
      HandlerAdapters 的初始化和HandlerMapping的类似
    private void initHandlerAdapters(ApplicationContext context) {
            this.handlerAdapters = null;
    
            if (this.detectAllHandlerAdapters) {
                // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
                Map<String, HandlerAdapter> matchingBeans =
                        BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
                if (!matchingBeans.isEmpty()) {
                    this.handlerAdapters = new ArrayList<HandlerAdapter>(matchingBeans.values());
                    // We keep HandlerAdapters in sorted order.
                    AnnotationAwareOrderComparator.sort(this.handlerAdapters);
                }
            }
            else {
                try {
                    HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
                    this.handlerAdapters = Collections.singletonList(ha);
                }
                catch (NoSuchBeanDefinitionException ex) {
                    // Ignore, we'll add a default HandlerAdapter later.
                }
            }
    
            // Ensure we have at least some HandlerAdapters, by registering
            // default HandlerAdapters if no other adapters are found.
            if (this.handlerAdapters == null) {
                this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
                if (logger.isDebugEnabled()) {
                    logger.debug("No HandlerAdapters found in servlet '" + getServletName() + "': using default");
                }
            }
        }
    

    相关文章

      网友评论

          本文标题:DispatcherServlet初始化

          本文链接:https://www.haomeiwen.com/subject/gqryuqtx.html