SpringMVC源码阅读-Servlet WebApplicationContext初始化(二)

说明

spring mvc 入口是通过配置Servlte来作为框架入口。servlte可以配置多个,每个Servlte都会初始化一个WebApplicationContext parent为root

注:httpServlet的init是 第一次访问的时候调用 

配置例子

<servlet>
    <servlet-name>spring</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 可以自定义servlet.xml配置文件的位置和名称,默认为WEB-INF目录下,名称为[<servlet-name>]-servlet.xml,如spring-servlet.xml
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring-servlet.xml</param-value> // 默认
    </init-param>
    -->
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet>
    <servlet-name>spring2</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 可以自定义servlet.xml配置文件的位置和名称,默认为WEB-INF目录下,名称为[<servlet-name>]-servlet.xml,如spring-servlet.xml
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring-servlet.xml</param-value> // 默认
    </init-param>
    -->
    <load-on-startup>2</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>spring</servlet-name>
    <url-pattern>*/.springDo</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>spring2</servlet-name>
    <url-pattern>*/.spring2Do</url-pattern>
  </servlet-mapping>

类图

 

  1. GenericServlet 抽象类 提供简单的getInitParameter,getInitParameterNames,getServletContext等几方法内部都是通过config获取 同时将servlet的有参init重写 提供一个无参init给子类实现
  2. HttpServlet     抽象类重写了service方法 根据http协议根据请求方式路由到对应的业务处理方法 是抽象的需要子类实现
  3. HttpServletBean ,负责将 ServletConfig 设置到当前 Servlet 对象中。
  4. FrameworkServlet ,负责初始化 Spring Servlet WebApplicationContext 容器。
  5. DispatcherServlet ,负责初始化 Spring MVC 的各个组件,以及处理客户端的请求
  6. ApplicationContextAware, EnvironmentAware 是spring接口 我们实现这2个接口 如果是spring初始化这2个类 就会注入容器和环境变量信息

HttpServletBean

org.springframework.web.servlet.HttpServletBean#init

这里是重写的GenericServlet提供的init方法 而不是servlet接口的

public final void init() throws ServletException {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Initializing servlet '" + this.getServletName() + "'");
        }
        try {
            /**
             * <1>
             * 将DispatcherServlet配置的 <init-param> 封装到PropertyValues
             */
            PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
            /**
             * <2>这里this 是DispatcherServlet  将他转为BeanWrapper 对象
             * beanWrapper是 spring框架内部操作java Bean的代理对象 实现依赖注入
             */
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
            //注册属性编辑器  暂时不知道干嘛的 需要读了spring源码
            bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
            //空实现 模板方法 子类可以实现做一些初始化操作
            this.initBeanWrapper(bw);
            //将pvs注入到BeanWrapper 这个也是代理的原因
            bw.setPropertyValues(pvs, true);
        } catch (BeansException var4) {
            this.logger.error("Failed to set bean properties on servlet '" + this.getServletName() + "'", var4);
            throw var4;
        }
        //<311>空实现 需要子类实现 进行后续初始化操作
        this.initServletBean();
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Servlet '" + this.getServletName() + "' configured successfully");
        }

    }

<2>beanwapper的简单使用方式

public class User {
    String userName;
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
}
public class BeanWrapperTest {
    public static void main(String[] args) {
        User user = new User();
        BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(user);
        bw.setPropertyValue("userName", "张三");
        System.out.println(user.getUserName());
        PropertyValue value = new PropertyValue("userName", "李四");
        bw.setPropertyValue(value);
        System.out.println(user.getUserName()); 
    }
}

FrameworkServlet

<311>initServletBean

org.springframework.web.servlet.HttpServletBean#init

->

org.springframework.web.servlet.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 {
            //<3> 初始化webApplication context
            this.webApplicationContext = this.initWebApplicationContext();
            //<4>模板方法 子类实现 framework并没有具体实现 是个空方法
            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");
        }

    }

<3>initWebApplicationContext

org.springframework.web.servlet.HttpServletBean#init

->

org.springframework.web.servlet.FrameworkServlet#initServletBean

->

org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext

protected WebApplicationContext initWebApplicationContext() {
        //获得 rootContext 内部是获取ServletContext的attribute key为WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
        WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
        WebApplicationContext wac = null;
        //<5>防止重复初始化 不等于空时表示构造函数直接传入
        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);
                    }
                    //<6>servlte解析配置的spring xml执行容器初始化
                    this.configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }

        if (wac == null) {
            /**
             * <7>这里主要通过servlteContext获取指定key的context
             */
            wac = this.findWebApplicationContext();
        }

        if (wac == null) {
            /**
             * <8>这里主要获取FrameworkServlet contextClass属性的值 默认是 XmlWebApplicationContext.class
             */
            wac = this.createWebApplicationContext(rootContext);
        }

        /**
         * <9>未触发刷新事件则触发 模板方法子类实现 dispatcher 做组件初始化动作
         */
        if (!this.refreshEventReceived) {
            this.onRefresh(wac);
        }

        /**
         * <10> 将 context 设置到 ServletContext 中
         */
        if (this.publishContext) {
            String attrName = this.getServletContextAttributeName();
            this.getServletContext().setAttribute(attrName, wac);
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Published WebApplicationContext of servlet '" + this.getServletName() + "' as ServletContext attribute with name [" + attrName + "]");
            }
        }

        return wac;
    }

<6>configureAndRefreshWebApplicationContext

    /**
     * <6>先留一个标记 后面看完spring源码再回来
     * @param wac
     */
    protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
        if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
            if (this.contextId != null) {
                wac.setId(this.contextId);
            } else {
                wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(this.getServletContext().getContextPath()) + "/" + this.getServletName());
            }
        }

        wac.setServletContext(this.getServletContext());
        wac.setServletConfig(this.getServletConfig());
        wac.setNamespace(this.getNamespace());
        wac.addApplicationListener(new SourceFilteringListener(wac, new FrameworkServlet.ContextRefreshListener()));
        ConfigurableEnvironment env = wac.getEnvironment();
        if (env instanceof ConfigurableWebEnvironment) {
            ((ConfigurableWebEnvironment)env).initPropertySources(this.getServletContext(), this.getServletConfig());
        }

        this.postProcessWebApplicationContext(wac);
        this.applyInitializers(wac);
        wac.refresh();
    }

 

<7>findWebApplicationContext

org.springframework.web.servlet.HttpServletBean#init

->

org.springframework.web.servlet.FrameworkServlet#initServletBean

->

org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext

->

org.springframework.web.servlet.FrameworkServlet#findWebApplicationContext

 protected WebApplicationContext findWebApplicationContext() {
        /**
         * 获得指定key 默认是空
         * <11>签名使用beanWarpper注入配置的属性 我们可以通过配置文件配置
         */
        String attrName = this.getContextAttribute();
        if (attrName == null) {
            return null;
        } else {
            //根据key从ServleContext的Attribute查找
            WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext(), attrName);
            if (wac == null) {
                throw new IllegalStateException("No WebApplicationContext found: initializer not registered?");
            } else {
                return wac;
            }
        }
    }
}

<11>使用例子

<servlet>
    <servlet-name>wemall</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>
        classpath:application-mvc.xml
      </param-value>
    </init-param>
    <init-param>
      <param-name>contextAttribute</param-name>
      <param-value>contextKey</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

<8>createWebApplicationContext

org.springframework.web.servlet.HttpServletBean#init

->

org.springframework.web.servlet.FrameworkServlet#initServletBean

->

org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext

->

org.springframework.web.servlet.FrameworkServlet#createWebApplicationContext(org.springframework.context.ApplicationContext)

protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
        /**
         * <12>从ContextClass 属性获取 容器的class 默认XMLWebApplicationContext
         */
        Class<?> contextClass = this.getContextClass();
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Servlet with name '" + this.getServletName() + "' will try to create custom WebApplicationContext context of class '" + contextClass.getName() + "'" + ", using parent context [" + parent + "]");
        }
        //判断是否是ConfigurableWebApplicationContext类型
        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
            throw new ApplicationContextException("Fatal initialization error in servlet with name '" + this.getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext");
        } else {
            //反射创建
            ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
            wac.setEnvironment(this.getEnvironment());
            wac.setParent(parent);
            wac.setConfigLocation(this.getContextConfigLocation());
            //<6>执行容器初始化操作
            this.configureAndRefreshWebApplicationContext(wac);
            return wac;
        }
    }

<12>例子

 public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
        public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;
        .....

        public FrameworkServlet() {
            this.contextClass = DEFAULT_CONTEXT_CLASS;
            .....
        }
    }

DispatcherServlet 

<9>onRefresh

org.springframework.web.servlet.HttpServletBean#init

->

org.springframework.web.servlet.FrameworkServlet#initServletBean

->

org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext

->

org.springframework.web.servlet.DispatcherServlet#onRefresh

protected void onRefresh(ApplicationContext context) {
        this.initStrategies(context);
    }
    /**
     * 初始化spring mvc组件
     * @param context
     */
    protected void initStrategies(ApplicationContext context) {
        //初始化上传文件Resolver
        this.initMultipartResolver(context);
        //初始化国际化Resolver 不同区域显示不同视图
        this.initLocaleResolver(context);
        //初始化控制页面主题的Resolver
        this.initThemeResolver(context);
        //初始化url映射 mapping
        this.initHandlerMappings(context);
        //初始化Handler适配器
        this.initHandlerAdapters(context);
        //初始化全局异常处理HandlerExceptionResolver
        this.initHandlerExceptionResolvers(context);
        this.initRequestToViewNameTranslator(context);
        this.initViewResolvers(context);
        this.initFlashMapManager(context);
    }

总结

1.服务器启动会调用servlet的init方法  对应入口就是HttpServlte

2.HttpServlteBean会负责将配置的变量通过BeanWarpper注入到DispactcherServlet

3.然后交给FrameworkServlet 做容器webApplication容器的创建

4.最后交给DispacherServlet做组件初始化

posted @ 2020-01-14 14:48  意犹未尽  阅读(298)  评论(0编辑  收藏  举报