• 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做组件初始化

  • 相关阅读:
    openlayers之标注功能四:聚合标注
    openlayers加载百度地图作为底图坐标偏移的解决办法
    设置bootstrap按钮组选中状态
    openlayers F11全屏显示
    8000401a 因为配置标识不正确,系统无法开始服务器进程。请检查用户名和密码
    IDA使用之旅(一)用IDA查看最简单的sys文件
    IDA使用之旅(三)实践中使用IDA工具
    IDA使用之旅(二)工具及窗口的使用
    正则表达式26英文字母大小写互转
    Ollydbg 中断方法
  • 原文地址:https://www.cnblogs.com/LQBlog/p/12191970.html
Copyright © 2020-2023  润新知