[置顶] 详解spring-MVC DispatcherServlet初始化

DispatcherServlet要是用过springmvc应该都知道这个的重要性,在我们的web.xml文件中会配置这个Servlet相当于我们springmvc中央处理器。

[置顶] 详解spring-MVC DispatcherServlet初始化_第1张图片
  我们这里详细分析DispatcherServlet的源码,DispatcherServlet的类结构图如下:
  [置顶] 详解spring-MVC DispatcherServlet初始化_第2张图片
  

这里有三个接口比较重要:EnvironmentCapable,EcvironmentAware和ApplicationContextAware。在spring中XXXAware在spring里面表示对XXX可以感知,通俗一点就是:如果在某个类里面想要使用spring的一些东西,就可以通过实现XXXAware接口告诉spring,spring看到后就会给你送过来,而接收的方式是通过实现接口唯一的setXXX。比如有一个类想要使用当前的ApplicationContext,那么我们只需要让它实现ApplicationContextAware接口,然后实现接口中唯一的方法void setApplicationContext(ApplicationContext applicationContext),spring会自动传入进来。而EnvironmentCapable恰好,他是用来提供环境的Environment getEnvironment();
  什么是环境呢?在我们Environment中封装了ServletContext,同时还封装了ServletConfig,JndiProperty,系统环境变量和系统属性。
看完我们的整体结构接下来就是具体的三个类的处理过程。

HttpServletBean

对一个Servlet进行分析我们需要首先对init无参方法进行分析下面是代码

 public final void init() throws ServletException {
        if(this.logger.isDebugEnabled()) {
            this.logger.debug("Initializing servlet \'" + this.getServletName() + "\'");
        }

        try {
            //获取配置的初始化值
            HttpServletBean.ServletConfigPropertyValues ex = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            ServletContextResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
            bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
            //模板方法,可以在子类调用,做一些初始化工作
            this.initBeanWrapper(bw);
            //将配置的初始化值(如contextConfigLocation)设置到DispatcherServlet
            bw.setPropertyValues(ex, true);
        } catch (BeansException var4) {
            this.logger.error("Failed to set bean properties on servlet \'" + this.getServletName() + "\'", var4);
            throw var4;
        }
        //模板方法
        this.initServletBean();
        if(this.logger.isDebugEnabled()) {
            this.logger.debug("Servlet \'" + this.getServletName() + "\' configured successfully");
        }

    }

在HttpServletBean的Init中,首先将Servlet中配置的参数使用BeanWrapper(用来操作JavaBean的一个工具),设置到DispathcerServlet的相关属性,然后调用模板方法InitServletBean,子类就通过这个方法初始化。

FrameworkServlet

通过上面的分析可以知道,FrameworkServlet的初始化入口方法应该是initServletBean,其代码如下:

protected final void initServletBean() throws ServletException {
this.getServletContext().log(“Initializing Spring FrameworkServlet \’” + this.getServletName() + “\’”);
if(this.logger.isInfoEnabled()) {
this.logger.info(“FrameworkServlet \’” + this.getServletName() + “\’: initialization started”);
}

    long startTime = System.currentTimeMillis();

    try {
        //初始化WebApplicationContext
        this.webApplicationContext = this.initWebApplicationContext();
        //初始化FrameworkServlet
        this.initFrameworkServlet();
    } catch (ServletException var5) {
        this.logger.error("Context initialization failed", var5);
        throw var5;
    } catch (RuntimeException var6) {
        this.logger.error("Context initialization failed", var6);
        throw var6;
    }

    if(this.logger.isInfoEnabled()) {
        long elapsedTime = System.currentTimeMillis() - startTime;
        this.logger.info("FrameworkServlet \'" + this.getServletName() + "\': initialization completed in " + elapsedTime + " ms");
    }

}
在initServletBean()中只有两个作用一个是初始化webApplicationContext一个是初始化FrameworkServlet。
initWebApplicationContext()方法有:
protected WebApplicationContext initWebApplicationContext() {
    //获取rootContext
        WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
        WebApplicationContext wac = null;
        //如果已经通过构造方法已经设置了webApplicationContext
        if(this.webApplicationContext != null) {
            wac = this.webApplicationContext;
            if(wac instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext attrName = (ConfigurableWebApplicationContext)wac;
                if(!attrName.isActive()) {
                    if(attrName.getParent() == null) {
                        attrName.setParent(rootContext);
                    }
                    //设置并刷新容器
                    this.configureAndRefreshWebApplicationContext(attrName);
                }
            }
        }

        if(wac == null) {
            //通过contextAttribute参数获取
            wac = this.findWebApplicationContext();
        }

        if(wac == null) {
            //手动创建一个webApplicationContext
            wac = this.createWebApplicationContext((WebApplicationContext)rootContext);
        }

        if(!this.refreshEventReceived) {
            //如果没刷新就刷新
            this.onRefresh(wac);
        }

        if(this.publishContext) {
            //将ApplicationContext保存到ServletContext中
            String attrName1 = this.getServletContextAttributeName();
            this.getServletContext().setAttribute(attrName1, wac);
            if(this.logger.isDebugEnabled()) {
                this.logger.debug("Published WebApplicationContext of servlet \'" + this.getServletName() + "\' as ServletContext attribute with name [" + attrName1 + "]");
            }
        }

        return wac;
    }

initWebApplicationContext方法做了三件事:
1。获取spring的根容器rootContext。
2.设置webApplicationContext并根据情况调用onRefresh方法。
3。将webApplicationContext设置到ServletContext中。

获取spring的跟容器roootContext

获取根容器的原理是将自己容器
sc.getAttribute(“org.springframework.web.context.WebApplicationContext.ROOT”);

设置webApplicationContext并根据情况调用onRefresh方法

设置webApplicationContext有三种方法。
第一种方法是在构造方法中已经传递webAppliicationContext参数,这时只需要对其进行一些设置。在Servlet3.0以后的环境中,Servlet3.0之后可以在程序中使用ServletContext.addServlet方式注册Servlet,这时就可以在新建FrameworkServlet和其子类的时候通过构成方法传递已经准备好的webApplicationContext。
第二种方式是webApplicationContext已经在ServletContext中了。这时只需要在配置Servlet的时候将ServketContext中的webApplicationContext的name配置到contextAttribute属性就可以了。

<init-param>
            <param-name>contextAttribute</param-name>
            <param-value>1</param-value>
        </init-param>

第三种方法是在前面两种方式都无效的情况下自己创建一个。正常情况下就是使用的这种方式。创建过程在createWebApplicationContext方法中。

这里要说明一下什么是WebApplicationContext。从名字上面来看他是web应用上下文容器 其中包含了我们spring-MVC配置文件中所有配置的东西初始化。

DispatcherServlet

onRefresh是DispatcherServlet的入口方法。onRefresh中简单地调用了initStrategies中调用了9个初始化方法。

    protected void onRefresh(ApplicationContext context) {
        this.initStrategies(context);
    }

    protected void initStrategies(ApplicationContext context) {
        this.initMultipartResolver(context);
        this.initLocaleResolver(context);
        this.initThemeResolver(context);
        this.initHandlerMappings(context);
        this.initHandlerAdapters(context);
        this.initHandlerExceptionResolvers(context);
        this.initRequestToViewNameTranslator(context);
        this.initViewResolvers(context);
        this.initFlashMapManager(context);
    }

具体是初始化9个组件。
分析以LocaleResolver为例

private void initLocaleResolver(ApplicationContext context) {
        try {
            this.localeResolver = (LocaleResolver)context.getBean("localeResolver", LocaleResolver.class);
            if(this.logger.isDebugEnabled()) {
                this.logger.debug("Using LocaleResolver [" + this.localeResolver + "]");
            }
        } catch (NoSuchBeanDefinitionException var3) {
            this.localeResolver = (LocaleResolver)this.getDefaultStrategy(context, LocaleResolver.class);
            if(this.logger.isDebugEnabled()) {
                this.logger.debug("Unable to locate LocaleResolver with name \'localeResolver\': using default [" + this.localeResolver + "]");
            }
        }

    }

在初始化方式中分两步:首先通过context.getBean在容器里面按注册时的名称或类型进行查找,所以在Spring MVC的配置文件中需要配置相应类型的组件,容器就可以自动找到。如果找不到就调用默认组件,这里的context指的是web应用上下文。这里的默认组件只会有8个没有文件上传,因为并不是所有都需要默认上传。

你可能感兴趣的:(spring,mvc,servlet)