11.0、Spring源码学习 ——SpringMVC 的 ContextLoaderListener 妖狐艹你老母 2021-10-18 20:10 380阅读 0赞 ### 文章目录 ### * * * 前言 * SpringMVC 和 web.xml * ServletContextListener * * 自己实现一个 ServletContextListener * * 自定义监听器的妙用 * 在 web.xml 中使用 < listener> 配置 ServletContextListener * SpringMVC 对 ServletContextListener 的实现 ContextLoaderListener * * ContextLoaderListener 的 UML * org.springframework.web.context.ContextLoaderListener 处理逻辑 UML * ContextLoader 决定 WebApplicationContext 的具体实现类 * * 仅允许加载一次 WebApplicationContext 对象 * 默认加载配置文件 ContextLoader.properties * 你可以自定义 WebApplicationContext 的实现类 * ContextLoaderListener 默认加载配置文件路径:/WEB-INF/applicationContext.xml * * 自定义 配置文件路径:web.xml增加一个全局参数配置 context-param:contextConfigLocation * ContextLoaderListener 的配置可以省略,但不建议这么做 * 源码跟踪 * * 入口 ContextLoaderListener.contextInitialized( * ContextLoader.initWebApplicationContext( * ContextLoader.createWebApplicationContext( * ContextLoader.configureAndRefreshWebApplicationContext( * StandardServletEnvironment.initPropertySources( * * servletConfigInitParams * servletContextInitParams * jndiProperties * systemProperties * systemEnvironment * ContextLoader.customizeContext( * AbstractApplicationContext.refresh( ### 前言 ### > 体能状态先于精神状态,习惯先于决心,聚焦先于喜好。 ### SpringMVC 和 web.xml ### > SpringMVC 的入口在 web.xml > 在该文件中我们可以进行多项配置,比如全局变量、过滤器、 < listener> 和 Spring 的 DispatcherServlet 等等。 ### ServletContextListener ### > 该接口的完整路径为 javax.servlet.ServletContextListener > 从源码注释中我们可以知道: > 1、一个类实现一个ServletContextListener 借口,这种实现可以有多个; > 2、该接口有两个方法 contextInitialized 和 contextDestroyed,二者都可以直接访问 ServletContext,ServletContext 是整个web 项目启动阶段的核心所在 > 3、contextInitialized 会在 ServletContext 初始化阶段运行,且早于 filters 或 servlets 的配置运行,期间我们完全可以做一些前期准备工作,比如配置文件加载、健康检查、数据库密码解密等等 > 4、contextDestroyed 会在 ServletContext 停止阶段运行,且晚于 filters 或 servlets 的配置运行,其运行对一些资源进行销毁。 public interface ServletContextListener extends EventListener { /** * 在 web 应用初始化阶段会调用本方法; * 可以使用多个类实现该接口,即你可以在一个应用中拥有多个 ServletContextListener; * ServletContextListener的contextInitialized方法会在 web.xml 中所有的 filters 或 servlets 配置前运行 * 参数 sce the ServletContextEvent 包含正在初始化的 ServletContext,即你可以在该方法的实现中直接操作 ServletContext */ public void contextInitialized(ServletContextEvent sce); /** * 当 ServletContext 将要停止时被调用 * * 在web.xml所有的 servlets 和 filters 配置执行完毕并销毁后 contextDestroyed 方法才会被执行 * 参数 sce 可以访问正在被销毁的 ServletContext 对象,即你可以在方法实现中操作 ServletContext 对象 */ public void contextDestroyed(ServletContextEvent sce); } #### 自己实现一个 ServletContextListener #### package com.bestcxx.cn.webrecord.listener; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; /** * 自定义一个 ServletContextListener * @Author jie.wu */ public class MyServletContextListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { System.out.println(this.getClass().getName()+"输出了"); System.out.println(this.getClass().getName()+"输出了"); System.out.println(this.getClass().getName()+"输出了"); System.out.println(this.getClass().getName()+"输出了"); System.out.println(this.getClass().getName()+"输出了"); //定义一般参数 sce.getServletContext().setAttribute("name","jecket"); //定义初始化类型参数,比如数据库连接的用户名密码解密等 sce.getServletContext().setInitParameter("demo","demo"); } @Override public void contextDestroyed(ServletContextEvent sce) { //todo } } ##### 自定义监听器的妙用 ##### > 有时候系统中存在线程池,如果在系统关闭的时候不回收线程池,最后可能造成内存泄漏,端口占用问题,所以,我们可以使用监听器在 contextDestroyed 进行线程池资源的回收工作. #### 在 web.xml 中使用 < listener> 配置 ServletContextListener #### > 实现了 ServletContextListener 的类后还需增加配置才能生效 > 通过 web.xml < listener> 标签指定相关类,可以有多个< listener>标签并列存在: <listener> <listener-class>com.bestcxx.cn.webrecord.listener.MyServletContextListener</listener-class> </listener> > 其中 < listener-class> 标签包围着一个类,该类实现了 ServletContextListener 接口。 > 这个类你也可以自己写,并且,一个web.xml 文件可以包含多个 < listener> 代码组合 ### SpringMVC 对 ServletContextListener 的实现 ContextLoaderListener ### > SpringlMVC 对 ServletContextListener 的实现是 ContextLoaderListener,其包含了更多的SpringMVC 自身的特性。 > 其最主要的功能就是加载 SpringMVC 相关的配置文件,进行初始化工作,核心逻辑是初始化 WebApplicationContext 实例并存放至 ServletContext 中。 <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> #### ContextLoaderListener 的 UML #### ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2Jlc3RjeHg_size_16_color_FFFFFF_t_70] #### org.springframework.web.context.ContextLoaderListener 处理逻辑 UML #### ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2Jlc3RjeHg_size_16_color_FFFFFF_t_70 1] #### ContextLoader 决定 WebApplicationContext 的具体实现类 #### > 如果你不指定,就会采用 Spring 自带的配置文件的类 XmlWebApplicationContext 进行实例化,除非你自己实现了个性的实例化类。 > 实例化后的 WebApplicationContext 会被放入到上下文中供应用使用。 ##### 仅允许加载一次 WebApplicationContext 对象 ##### > 当加载完毕后被实例化的 WebApplicationContext 对象会被保存到 上下文中 //值为 org.springframework.web.context.WebApplicationContext.ROOT WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE= WebApplicationContext.class.getName() + ".ROOT" //···中间代码略 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); > 加载仅允许加载一次,即你只能在 web.xml 中配置一次 ContextLoaderListener if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) { throw new IllegalStateException( "Cannot initialize context because there is already a root application context present - " + "check whether you have multiple ContextLoader* definitions in your web.xml!"); } ##### 默认加载配置文件 ContextLoader.properties ##### > 注意UML中的 ContextLoader,该类的静态方法加载了一个默认的配置文件 ContextLoader.properties ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2Jlc3RjeHg_size_16_color_FFFFFF_t_70 2] static { // Load default strategy implementations from properties file. // This is currently strictly internal and not meant to be customized // by application developers. try { ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class); defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); } catch (IOException ex) { throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage()); } } > ContextLoader.properties 文件内容只有一行,即默认情况下 WebApplicationContext 的实现类为 XmlWebApplicationContext org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext ##### 你可以自定义 WebApplicationContext 的实现类 ##### > 对于 WebApplicationContext 的具体实现类,你可以通过自定义的ServletContextListener 或者全局变量提前将变量放入上下文中,名称为 contextClass, > 它就会使用你提供的类为 WebApplicationContext 提供一个实例化对象。 > 当然,你没必要自己写,如果仅仅作为实验的话——看看报错信息也可以,如果不想看报错信息就直接将value 写成 org.springframework.web.context.support.XmlWebApplicationContext * 自定义 ServletContextListener > 注意是 setInitParameter @Override public void contextInitialized(ServletContextEvent sce) { //定义一般参数 sce.getServletContext().setAttribute("name","jecket"); //定义初始化类型参数-指定WebApplicationContext 的具体实例化类 //org.springframework.web.context.support.XmlWebApplicationContext 是默认的类,在 ContextLoader.properties 中有指定 sce.getServletContext().setInitParameter("contextClass","com.bestcxx.cn.webrecord.listener.MyWebApplicationContext"); } * 通过全局变量 在web.xml 中 <context-param> <param-name>contextClass</param-name> <param-value>com.bestcxx.cn.webrecord.listener.MyWebApplicationContext</param-value> </context-param> * Spring 源码通过反射机制为 WebApplicationContext 生成实例化对象 > 如果你指定了具体实例化类,就用你指定的,否则就用默认的 public static final String CONTEXT_CLASS_PARAM = "contextClass"; //···中间代码略 protected Class<?> determineContextClass(ServletContext servletContext) { String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); if (contextClassName != null) { try { return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); } catch (ClassNotFoundException ex) { throw new ApplicationContextException( "Failed to load custom context class [" + contextClassName + "]", ex); } } else { contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); try { return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader()); } catch (ClassNotFoundException ex) { throw new ApplicationContextException( "Failed to load default context class [" + contextClassName + "]", ex); } } } ### ContextLoaderListener 默认加载配置文件路径:/WEB-INF/applicationContext.xml ### > 默认情况下,WebApplicationContext 由 XmlWebApplicationContext 实例化后,就会加载配置文件 > ,XmlWebApplicationContext 默认配置文件路径为 /WEB-INF/applicationContext.xml 文件,路径和名字都必须一模一样。 > applicationContext.xml 内应该包含的内容是 Spring 容器基础能力相关的内容-比如数据库、业务、事务等功能bean,而不应该包含 controller 和页面的内容. public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext { /** Default config location for the root context */ public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml"; #### 自定义 配置文件路径:web.xml增加一个全局参数配置 context-param:contextConfigLocation #### > 表达式支持1,2 挥着 \* 通配符,即可以配置多个文件 <!--自定义SpringMVC 配置文件的位置 1、contextConfigLocation 名字不可变 2、可以使用通配符加载多个文件,如 classpath:*-applicationContext.xml--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> #### ContextLoaderListener 的配置可以省略,但不建议这么做 #### > 如果你的 web.xml 没有配置 ContextLoaderListener 的 < listener>,你会发现你的 SpringMVC 项目应用并不受什么影响。因为 SpringMVC 的 DispatcherServlet 也可以指定加载多个配置文件,但是不建议这么做,因为正常来说 ContextLoaderListener 负责加载基础功能的bean,会提前准备好,DispatcherServlet 加载 servlet相关的Bean,比如Controller 拦截器、视图处理器等. > 关于 DispatcherServlet 的配置请移步:[SpringMVC 的 DispatcherServet][SpringMVC _ DispatcherServet] ### 源码跟踪 ### #### 入口 ContextLoaderListener.contextInitialized( #### > web应用启动阶段会自动运行 web.xml 中配置的 < listener> > org.springframework.web.context.ContextLoaderListener.contextInitialized > 注意 initWebApplicationContext(event.getServletContext()); /** * 初始化web应用的根上下文 */ @Override public void contextInitialized(ServletContextEvent event) { //初始化 webApplicationContext initWebApplicationContext(event.getServletContext()); } #### ContextLoader.initWebApplicationContext( #### > org.springframework.web.context.ContextLoader.initWebApplicationContext( > 为给定的 servlet 上下文对象 初始化 Spring webAppictionContext对象,该webAppictionContext 可以在构造函数中提供,也可以创建一个新的对象,依据位于 > CONTEXT\_CLASS\_PARAM 和 contextConfigLocation 这两个全局配置。 > 注意 createWebApplicationContext(servletContext); 这一句 > 注意 configureAndRefreshWebApplicationContext(cwac, servletContext);这一句 > * 实例化 WebApplicationContext 对象,默认是 XmlWebApplicationContext > * XmlWebApplicationContext 间接实现了 ConfigurableWebApplicationContext ,如果该对象尚未 refreshed,则判断是否设置了 根web上下文对象,如果webApplicationContext 对象没有明确指定 父上下文对象,则确定一个根web上下文对象——只要符合标准即可 /** * @param servletContext current servlet context * @return the new WebApplicationContext * @see #ContextLoader(WebApplicationContext) * @see #CONTEXT_CLASS_PARAM * @see #CONFIG_LOCATION_PARAM */ public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { //以 org.springframework.web.context.WebApplicationContext.ROOT 作为key保存 webApplicationContext 对象,该对象仅允许加载一次,所以这里先判断一下是否已经加载过 if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) { throw new IllegalStateException( "Cannot initialize context because there is already a root application context present - " + "check whether you have multiple ContextLoader* definitions in your web.xml!"); } Log logger = LogFactory.getLog(ContextLoader.class); servletContext.log("Initializing Spring root WebApplicationContext"); if (logger.isInfoEnabled()) { logger.info("Root WebApplicationContext: initialization started"); } long startTime = System.currentTimeMillis(); try { //将 上下文对象 保存到本地可以访问的实例中,以确保该上下文对象在服务停止时可以访问到 if (this.context == null) { this.context = createWebApplicationContext(servletContext); } if (this.context instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context; if (!cwac.isActive()) { // 如果 appliactionContext 对象还没有被 refreshed (refreshed 会提供父上下文对象,设置上下文id等配置),该值在 AbstractApplicationContext.refresh()的prepareRefresh()中修改为true if (cwac.getParent() == null) { //webApplicationContext 对象没有明确指定 父上下文对象,则确定一个根web上下文对象——只要符合标准即可 ApplicationContext parent = loadParentContext(servletContext); //debug 发现,默认情况下这个值为null cwac.setParent(parent); } //进行 webApplicationContext 的 配置和refresh 工作 configureAndRefreshWebApplicationContext(cwac, servletContext); } } //初始化完毕后,放入到 servletContext 对象中,之后的Servlet请求就可以使用了 // key 为 org.springframework.web.context.WebApplicationContext.ROOT servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); ClassLoader ccl = Thread.currentThread().getContextClassLoader(); if (ccl == ContextLoader.class.getClassLoader()) { currentContext = this.context; } else if (ccl != null) { currentContextPerThread.put(ccl, this.context); } if (logger.isDebugEnabled()) { logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" + WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]"); } if (logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms"); } return this.context; } catch (RuntimeException ex) { logger.error("Context initialization failed", ex); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex); throw ex; } catch (Error err) { logger.error("Context initialization failed", err); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err); throw err; } } #### ContextLoader.createWebApplicationContext( #### > org.springframework.web.context.ContextLoader.createWebApplicationContext( > 为这个加载器初始化 根 WebApplicationContext 对象,也可以是 默认的或者指定的上下文类 。 > 指定的类被期望为 ConfigurableWebApplicationContext 的子类,并且该方法不可被覆盖重写。 > 此外,在 refreshing the context 这个webApplicaionContext 对象之前,允许子类提前调用 customizeContext()方法,增加一些定制化的定义。 > * 该方法内部将进行判断,如果你没有指定特殊的 webApplicationContext 的实例化类,则将使用Spring 默认的实例化类 org.springframework.web.context.support.XmlWebApplicationContext 来进行实例化,方式为 ClassUtils.forName > * 需要注意的是,实例化的类必须(间接)实现了 ConfigurableWebApplicationContext 的接口,默认的XmlWebApplicationContext 这个类是没有问题的,如果自己指定的话千万注意 ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2Jlc3RjeHg_size_16_color_FFFFFF_t_70 3] /** * @param sc current servlet context * @return the root WebApplicationContext * @see ConfigurableWebApplicationContext */ protected WebApplicationContext createWebApplicationContext(ServletContext sc) { //该方法内部将进行判断,如果你没有指定特殊的 webApplicationContext 的实例化类,则将使用Spring 默认的实例化类 org.springframework.web.context.support.XmlWebApplicationContext 来进行实例化,方式为 ClassUtils.forName Class<?> contextClass = determineContextClass(sc); //需要注意的是,实例化的类必须是 ConfigurableWebApplicationContext 的子类 if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); } return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); } #### ContextLoader.configureAndRefreshWebApplicationContext( #### > org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext( > 对于 新创建的 webApplicationContext 对象进行配置和刷新操纵-设置 context的id、初始化 servletContext的环境变量、refresh 等等。 > * `initPropertySources(` protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) { if (ObjectUtils.identityToString(wac).equals(wac.getId())) { //为 servletContext 设置一个更有用的名字——而不是默认的名字,我理解的就是名字更通俗易懂 //从统一配置获取-如果你没有配置,这个就是null了 String idParam = sc.getInitParameter(CONTEXT_ID_PARAM); if (idParam != null) { wac.setId(idParam); } else { // 默认代码会走这里,最终 servletContext 名字和 applicationContext 对象有一定关联 wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath())); } } //为webApplicationContext 对象 设置 servletContext 对象 wac.setServletContext(sc); //从统一配置获取待加载的配置文件路径,比如本文配置为 classpath:applicationContext.xml,如果没有配置,默认就是 WEB-INF/applicationContext.xml String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM); if (configLocationParam != null) { wac.setConfigLocation(configLocationParam); } // 当上下文环境刷新时,webApplicationContext environment 的初始化 配置资源会被请求;这里使用卫语句判断是为了确保任何 servlet 配置资源在 refresh 之前的操作生效 ConfigurableEnvironment env = wac.getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { //提前加载一切资源,比如操作系统信息,JVM信息等等 //为 sevletContext 初始化环境变量 servletConfigInitParams,servletContextInitParams,jndiProperties,systemProperties,systemEnvironment ((ConfigurableWebEnvironment) env).initPropertySources(sc, null); } //初始化所有的上下文对象,debug发现,默认的上下文对象数量为0,即该方法代码默认情况下没有起作用 customizeContext(sc, wac); //最重要的一个方法,刷新上下文,这里是方法模板,由子类具体实现 wac.refresh(); } #### StandardServletEnvironment.initPropertySources( #### > org.springframework.web.context.support.StandardServletEnvironment.initPropertySources( > 从 getPropertySources() 可以粗略的看出加载的配置信息 > \[servletConfigInitParams,servletContextInitParams,jndiProperties,systemProperties,systemEnvironment\] @Override public void initPropertySources(ServletContext servletContext, ServletConfig servletConfig) { WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig); } ##### servletConfigInitParams ##### ![在这里插入图片描述][20190829231818642.png] ##### servletContextInitParams ##### ![在这里插入图片描述][20190829231742849.png] ##### jndiProperties ##### ![在这里插入图片描述][20190829231711173.png] ##### systemProperties ##### ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2Jlc3RjeHg_size_16_color_FFFFFF_t_70 4] ##### systemEnvironment ##### ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2Jlc3RjeHg_size_16_color_FFFFFF_t_70 5] #### ContextLoader.customizeContext( #### > org.springframework.web.context.ContextLoader.customizeContext( > 在 上下文 refrash 之前,在上下文定义了本地配置信息后,自定义 被这个 ContextLoader 创建的ConfigurableWebApplicationContext对象(webApplicationContext 对象) > 上下文初始化类可以通过全局配置参数 CONTEXT\_INITIALIZER\_CLASSES\_PARAM 指定,默认通过这个方法获得 determineContextInitializerClasses(ServletContext),并且通过 ApplicationContextInitializer 初始化每一个被提供的 web 应用上下文。 > 任何 ApplicationContextInitializers 实现了 org.springframework.core.Ordered Ordered 或者被org.springframework.core.annotation.Order Order 注解标注的类会被适当的排序。 /** * @param sc the current servlet context * @param wac the newly created application context * @see #CONTEXT_INITIALIZER_CLASSES_PARAM * @see ApplicationContextInitializer#initialize(ConfigurableApplicationContext) */ protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) { //获取上下文初始化类,,debug发现默认为0 List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses = determineContextInitializerClasses(sc); for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) { Class<?> initializerContextClass = GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class); if (initializerContextClass != null && !initializerContextClass.isInstance(wac)) { throw new ApplicationContextException(String.format( "Could not apply context initializer [%s] since its generic parameter [%s] " + "is not assignable from the type of application context used by this " + "context loader: [%s]", initializerClass.getName(), initializerContextClass.getName(), wac.getClass().getName())); } this.contextInitializers.add(BeanUtils.instantiateClass(initializerClass)); } AnnotationAwareOrderComparator.sort(this.contextInitializers); for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) { initializer.initialize(wac); } } #### AbstractApplicationContext.refresh( #### > org.springframework.context.support.AbstractApplicationContext.refresh > 这个方法太重要了,单独写一篇文章介绍。 > [认识 AbstractApplicationContext.refresh()][AbstractApplicationContext.refresh] [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2Jlc3RjeHg_size_16_color_FFFFFF_t_70]: /images/20211018/2297a59f3ab24861865944c1ed1af405.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2Jlc3RjeHg_size_16_color_FFFFFF_t_70 1]: /images/20211018/97c02b306ca64f51a023eca696bca91d.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2Jlc3RjeHg_size_16_color_FFFFFF_t_70 2]: /images/20211018/8669d768a6c841d0a297c7b1d5062c7e.png [SpringMVC _ DispatcherServet]: https://blog.csdn.net/bestcxx/article/details/98936049 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2Jlc3RjeHg_size_16_color_FFFFFF_t_70 3]: /images/20211018/dc9f66241659470bbd22b12a95b7e92c.png [20190829231818642.png]: /images/20211018/f3143a4703ed47b59daf3f5751a90b03.png [20190829231742849.png]: /images/20211018/f31050cfb94741e1b2437b91fc1dc190.png [20190829231711173.png]: /images/20211018/80be57696fc84396977916b5ba064d5f.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2Jlc3RjeHg_size_16_color_FFFFFF_t_70 4]: /images/20211018/1c1bb5975d8f4cc796c5addddce66769.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2Jlc3RjeHg_size_16_color_FFFFFF_t_70 5]: /images/20211018/9a65da7aafb04a3d82fee7745895e279.png [AbstractApplicationContext.refresh]: https://blog.csdn.net/bestcxx/article/details/90517999
还没有评论,来说两句吧...