初始化DispatcherServlet并创建Spring容器

本文详细解释了在Tomcat启动时,如何通过web.xml配置ContextLoaderListener和DispatcherServlet,以及它们在初始化过程中与Spring容器交互的过程,包括Spring容器的创建、配置文件解析和属性设置等关键步骤。
1. 启动tomcat
2. 解析web.xml
   如果配置了
   <listener>
       <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
   </listener>
   这个类是spring提供了,它是一个servletLister,servlet的三大组件之一在tomcat启动的时候会调用
   ContextLoaderListener extends ContextLoader implements ServletContextListener{
        初始化方法
        public void contextInitialized(ServletContextEvent event) {
           创建Spring容器
           this.initWebApplicationContext(event.getServletContext());{
                this.context = this.createWebApplicationContext(servletContext);{
                    推断spring容器的类型
                    Class<?> contextClass = this.determineContextClass(sc);{
                        // 首先看有没有在web.xml中配置spring容器的类型
                        String contextClassName = servletContext.getInitParameter("contextClass");
                        if (contextClassName != null) {
                            return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
                        }else{
                            // 如果没有,就使用默认的解析xml文件的web容器
                            // org.springframework.web.context.WebApplicationContext = org.springframework.web.context.support.XmlWebApplicationContext
                            contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
                            return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
                        }
                    }
                    // 刷新容器,解析spring.xml,创建bean对象
                    this.configureAndRefreshWebApplicationContext(cwac, servletContext);{
                        // 将servletContext放入Spring容器中
                        wac.setServletContext(sc);
                        // 获取配置的spring.xml文件路径
                        String configLocationParam = sc.getInitParameter("contextConfigLocation");
                        if (configLocationParam != null) {
                            wac.setConfigLocation(configLocationParam);
                        }
                        // 刷新容器
                        wac.refresh();
                    }
                    // 将创建的spring容器放入servletContext中
                    // 默认名称为org.springframework.web.context.WebApplicationContext.ROOT
                    servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

                }
           }
       }
   }

3. 创建并初始化DispatcherServlet
3.1 调用DispatcherServlet.init(servlet生命周期方法) ->
DispatcherServlet extends FrameworkServlet extends HttpServletBean
    extends HttpServlet extend GenericServlet implements Servlet,ServletConfig

javax.servlet.GenericServlet.init
public void init(ServletConfig config) throws ServletException {
    this.config = config;
    // 这个方法是给子类重写的,在springmvc的HttpServletBean重写了
    this.init();
}
在HttpServlet并没有重写init方法
javax.servlet.http.HttpServlet extends GenericServlet
在HttpServletBean重写了
org.springframework.web.servlet.HttpServletBean extend HttpServlet{
    重写父类的init方法
    init(){
        // 找web.xml中的配置信息
        // 会将所有的init-params解析出来,通过名称进行反射执行set属性方法给对应的Servlet
        // 如果需要设置DispatcherServlet,则可以在配置DispatcherServlet中添加init-param
        // 因为DispatcherServlet是Tomcat创建的,所以我们没办法直接set
        // 如果要给它设置的初始化属性那就属性只能存在ServletContext或者ServletConfig中
        // 而web.xml正好提供了init-param来保存初始化属性,存在ServletConfig中,这样就达到了设置属性的效果
        PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);{
            // 通过获取web.xml中配置的init-params
            Enumeration<String> paramNames = config.getInitParameterNames();
            while (paramNames.hasMoreElements()) {
                // 将init-params的键值对获取出来,先保存起来
                String property = paramNames.nextElement();
                Object value = config.getInitParameter(property);
                // 保持在一个集合中,后面使用
                addPropertyValue(new PropertyValue(property, value));
            }
        }
        // 如果配置了初始化属性
        if (!pvs.isEmpty()) {
            // 包装成BeanWrapper对象
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            // 将值设置为Servlet的属性,通过反射,配置的名称,比如contextConfigLocation,detectAllHandlerMappings
            // 就是setDetectAllHandlerMappings,setContextConfigLocation,就将值复制给对应的Servlet
            // 上面我们配置的是DispatcherServlet,所以这里设置的也是DispatcherServlet的属性
            // 因为不同的Servlet有不同的init-param
            bw.setPropertyValues(pvs, true);
        }
        // 初始化Servlet相关的Bean
        initServletBean();{
            // 创建SpringMVC容器
            this.webApplicationContext = initWebApplicationContext();{
                // 找已经创建的spring容器,看一下是否找的的,如果配置了ContextLoaderListener,就能找到,第二步有讲到
                WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());{
                   // 就是上面存入的spring容器
                    WebApplicationContext attr =(WebApplicationContext) sc.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
                }
                WebApplicationContext wac = null;
                if (wac == null) {
                    wac = createWebApplicationContext(rootContext);{
                        // 获取springmvc容器的类型,默认为public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;
                        // 可以通过setContextClass修改容器的类型,默认是通过xml来创建容器
                        Class<?> contextClass = this.getContextClass();
                        // 创建springmvc容器
                        ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
                        // 将rootContext(也就是spring容器)设置为springmvc的父容器
                        wac.setParent(parent);
                        // 获取配置的init-params的contextConfigLocation,在之前就已经保存了
                        // 通过反射设置的ContextConfigLocation,因为已经配置的ContextConfigLocation的值
                        // 只需要在ContextConfigLocation中添加set,就可以返回执行setContextConfigLocation方法
                        String configLocation = this.getContextConfigLocation();
                        // 设置需要解析的springmvc配置文件
                        if (configLocation != null) {
                            wac.setConfigLocation(configLocation);
                        }
                        // 刷新springmvc容器
                        this.configureAndRefreshWebApplicationContext(wac);{
                            // 设置ServletContext和config到spring容器中
                            wac.setServletContext(this.getServletContext());
                            wac.setServletConfig(this.getServletConfig());
                            wac.setNamespace(this.getNamespace());
                            // 发布一个spring的事件,该监听器监听ContextRefreshedEvent
                            wac.addApplicationListener(new SourceFilteringListener(wac, new FrameworkServlet.ContextRefreshListener()));{
                                class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
                                    public void onApplicationEvent(ContextRefreshedEvent event) {
                                        FrameworkServlet.this.onApplicationEvent(event);{
                                            // 刷新web容器
                                            this.onRefresh(event.getApplicationContext());{
                                                // 这里才真正达到DispatcherServlet中,执行初始化操作
                                                // 初始化九大组件
                                                this.initStrategies(context);
                                            }
                                        }
                                    }
                                }
                            }
                            // 其实创建的就是StandardServletEnvironment
                            ConfigurableEnvironment env = wac.getEnvironment();{
                                 public AbstractEnvironment() {
                                   this(new MutablePropertySources());
                                }
                                protected AbstractEnvironment(MutablePropertySources propertySources) {
                                    this.propertySources = propertySources;
                                    // 配置属性,添加默认的属性
                                    this.customizePropertySources(propertySources);{
                                        // 将servletConfigInitParams,servletContextInitParams放入环境中,当成属性
                                        // 后面会将StubPropertySource改成对应的ServletContextPropertySource/ServletConfigPropertySource
                                        // 这里只是占位
                                        propertySources.addLast(new StubPropertySource("servletConfigInitParams"));
                                        propertySources.addLast(new StubPropertySource("servletContextInitParams"));
                                        if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
                                            propertySources.addLast(new JndiPropertySource("jndiProperties"));
                                        }
                                    }
                                }
                            }
                            // 加载属性,将ServletContext和ServletConfig存入环境对象的PropertySources中
                            if (env instanceof ConfigurableWebEnvironment) {
                                // 将默认的属性修改为对应servlet或者config的属性,因为上面创建环境的时候,使用的两个属性占位
                                ((ConfigurableWebEnvironment)env).initPropertySources(this.getServletContext(), this.getServletConfig());{
                                   WebApplicationContextUtils.initServletPropertySources(this.getPropertySources(), servletContext, servletConfig);{
                                   String name = "servletContextInitParams";
                                   if (servletContext != null && sources.get(name) instanceof StubPropertySource) {
                                        // 将上面的StubPropertySource替换为ServletContextPropertySource
                                       sources.replace(name, new ServletContextPropertySource(name, servletContext));
                                   }
                                   name = "servletConfigInitParams";
                                   if (servletConfig != null && sources.get(name) instanceof StubPropertySource) {
                                       sources.replace(name, new ServletConfigPropertySource(name, servletConfig));
                                   }
                                }
                            }
                            // 创建springmvc容器,并且设置了一些基本属性之后,进行的扩展操作,默认是空实现
                            this.postProcessWebApplicationContext(wac);
                            this.applyInitializers(wac);{
                                // 主要获取配置的初始化器,是获取spring提供的,在容器刷新前进行一些初始化操作
                                String globalClassNames = this.getServletContext().getInitParameter("globalInitializerClasses");
                                // 按照",;切割
                                String[] lis = StringUtils.tokenizeToStringArray(globalClassNames, ",; \t\n");
                                String className;
                                for(i = 0; i < lis.length; ++i) {
                                    className = lis[i];
                                    this.contextInitializers.add(this.loadInitializer(className, wac));
                                }
                                for(i = 0; i < lis.length; ++i) {
                                    // 执行初始化器
                                    ApplicationContextInitializer<ConfigurableApplicationContext> initializer = (ApplicationContextInitializer)var7.next();
                                    initializer.initialize(wac);
                                }
                            }
                            // 刷新容器
                            wac.refresh();
                        }
                        return wac;
                    }
                }
                if (this.publishContext) {
                    String attrName = getServletContextAttributeName();{
                        // public static final String SERVLET_CONTEXT_PREFIX = FrameworkServlet.class.getName() + ".CONTEXT.";
                       return SERVLET_CONTEXT_PREFIX + this.getServletName();
                    }
                    // 将springmvc容器放入servletContext中
                    getServletContext().setAttribute(attrName, wac);
                }
            }
            // 初始化一些FrameworkServlet()的东西,默认是空实现,是给子类实现的
            initFrameworkServlet();
        }

    }
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值