zoukankan      html  css  js  c++  java
  • Spring 学习记录7 初识XmlWebApplicationContext

    主题

    之前Spring相关的一些类,比如Enviromnent,BenFactory都接触了一些.有一些收获.但是直接看代码很多方法都不知为什么这样写.哪里会用到.因为太底层了.不好理解..现在从高层次的调用开始再研究下.应该会有新的理解.

    所以从一个Web应用初始化开始学习.看看它经历了哪些步骤.做了哪些事情.

    之前对spring的dispatcherServlet有一点点研究(http://www.cnblogs.com/abcwt112/p/5283674.html).

    ContextLoaderListener

    1个最普通的WEB项目如果要在servlet环境中用Spring.肯定是在web.xml里配置1个listener.这个linstener是1个入口,在内部肯定会创建Spring相关的applicationcontext并配置它.

      1 /*
      2  * Copyright 2002-2015 the original author or authors.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package org.springframework.web.context;
     18 
     19 import javax.servlet.ServletContextEvent;
     20 import javax.servlet.ServletContextListener;
     21 
     22 /**
     23  * Bootstrap listener to start up and shut down Spring's root {@link WebApplicationContext}.
     24  * Simply delegates to {@link ContextLoader} as well as to {@link ContextCleanupListener}.
     25  *
     26  * <p>This listener should be registered after {@link org.springframework.web.util.Log4jConfigListener}
     27  * in {@code web.xml}, if the latter is used.
     28  *
     29  * <p>As of Spring 3.1, {@code ContextLoaderListener} supports injecting the root web
     30  * application context via the {@link #ContextLoaderListener(WebApplicationContext)}
     31  * constructor, allowing for programmatic configuration in Servlet 3.0+ environments.
     32  * See {@link org.springframework.web.WebApplicationInitializer} for usage examples.
     33  *
     34  * @author Juergen Hoeller
     35  * @author Chris Beams
     36  * @since 17.02.2003
     37  * @see org.springframework.web.WebApplicationInitializer
     38  * @see org.springframework.web.util.Log4jConfigListener
     39  */
     40 public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
     41 
     42     /**
     43      * Create a new {@code ContextLoaderListener} that will create a web application
     44      * context based on the "contextClass" and "contextConfigLocation" servlet
     45      * context-params. See {@link ContextLoader} superclass documentation for details on
     46      * default values for each.
     47      * <p>This constructor is typically used when declaring {@code ContextLoaderListener}
     48      * as a {@code <listener>} within {@code web.xml}, where a no-arg constructor is
     49      * required.
     50      * <p>The created application context will be registered into the ServletContext under
     51      * the attribute name {@link WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE}
     52      * and the Spring application context will be closed when the {@link #contextDestroyed}
     53      * lifecycle method is invoked on this listener.
     54      * @see ContextLoader
     55      * @see #ContextLoaderListener(WebApplicationContext)
     56      * @see #contextInitialized(ServletContextEvent)
     57      * @see #contextDestroyed(ServletContextEvent)
     58      */
     59     public ContextLoaderListener() {
     60     }
     61 
     62     /**
     63      * Create a new {@code ContextLoaderListener} with the given application context. This
     64      * constructor is useful in Servlet 3.0+ environments where instance-based
     65      * registration of listeners is possible through the {@link javax.servlet.ServletContext#addListener}
     66      * API.
     67      * <p>The context may or may not yet be {@linkplain
     68      * org.springframework.context.ConfigurableApplicationContext#refresh() refreshed}. If it
     69      * (a) is an implementation of {@link ConfigurableWebApplicationContext} and
     70      * (b) has <strong>not</strong> already been refreshed (the recommended approach),
     71      * then the following will occur:
     72      * <ul>
     73      * <li>If the given context has not already been assigned an {@linkplain
     74      * org.springframework.context.ConfigurableApplicationContext#setId id}, one will be assigned to it</li>
     75      * <li>{@code ServletContext} and {@code ServletConfig} objects will be delegated to
     76      * the application context</li>
     77      * <li>{@link #customizeContext} will be called</li>
     78      * <li>Any {@link org.springframework.context.ApplicationContextInitializer ApplicationContextInitializer}s
     79      * specified through the "contextInitializerClasses" init-param will be applied.</li>
     80      * <li>{@link org.springframework.context.ConfigurableApplicationContext#refresh refresh()} will be called</li>
     81      * </ul>
     82      * If the context has already been refreshed or does not implement
     83      * {@code ConfigurableWebApplicationContext}, none of the above will occur under the
     84      * assumption that the user has performed these actions (or not) per his or her
     85      * specific needs.
     86      * <p>See {@link org.springframework.web.WebApplicationInitializer} for usage examples.
     87      * <p>In any case, the given application context will be registered into the
     88      * ServletContext under the attribute name {@link
     89      * WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE} and the Spring
     90      * application context will be closed when the {@link #contextDestroyed} lifecycle
     91      * method is invoked on this listener.
     92      * @param context the application context to manage
     93      * @see #contextInitialized(ServletContextEvent)
     94      * @see #contextDestroyed(ServletContextEvent)
     95      */
     96     public ContextLoaderListener(WebApplicationContext context) {
     97         super(context);
     98     }
     99 
    100 
    101     /**
    102      * Initialize the root web application context.
    103      */
    104     @Override
    105     public void contextInitialized(ServletContextEvent event) {
    106         initWebApplicationContext(event.getServletContext());
    107     }
    108 
    109 
    110     /**
    111      * Close the root web application context.
    112      */
    113     @Override
    114     public void contextDestroyed(ServletContextEvent event) {
    115         closeWebApplicationContext(event.getServletContext());
    116         ContextCleanupListener.cleanupAttributes(event.getServletContext());
    117     }
    118 
    119 }
    View Code

    既然是1个listener.spring相关步骤肯定写在listener的contextInitialized方法里.内部很简单的调用了父类的initWebApplicationContext方法,并传入了servletContext对象作为参数.看方法名就知道这个方法肯定是要初始化WebApplicationContext.

     1 public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
     2         if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
     3             throw new IllegalStateException(
     4                     "Cannot initialize context because there is already a root application context present - " +
     5                     "check whether you have multiple ContextLoader* definitions in your web.xml!");
     6         }
     7 
     8         Log logger = LogFactory.getLog(ContextLoader.class);
     9         servletContext.log("Initializing Spring root WebApplicationContext");
    10         if (logger.isInfoEnabled()) {
    11             logger.info("Root WebApplicationContext: initialization started");
    12         }
    13         long startTime = System.currentTimeMillis();
    14 
    15         try {
    16             // Store context in local instance variable, to guarantee that
    17             // it is available on ServletContext shutdown.
    18             if (this.context == null) {
    19                 this.context = createWebApplicationContext(servletContext);
    20             }
    21             if (this.context instanceof ConfigurableWebApplicationContext) {
    22                 ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
    23                 if (!cwac.isActive()) {
    24                     // The context has not yet been refreshed -> provide services such as
    25                     // setting the parent context, setting the application context id, etc
    26                     if (cwac.getParent() == null) {
    27                         // The context instance was injected without an explicit parent ->
    28                         // determine parent for root web application context, if any.
    29                         ApplicationContext parent = loadParentContext(servletContext);
    30                         cwac.setParent(parent);
    31                     }
    32                     configureAndRefreshWebApplicationContext(cwac, servletContext);
    33                 }
    34             }
    35             servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
    36 
    37             ClassLoader ccl = Thread.currentThread().getContextClassLoader();
    38             if (ccl == ContextLoader.class.getClassLoader()) {
    39                 currentContext = this.context;
    40             }
    41             else if (ccl != null) {
    42                 currentContextPerThread.put(ccl, this.context);
    43             }
    44 
    45             if (logger.isDebugEnabled()) {
    46                 logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
    47                         WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
    48             }
    49             if (logger.isInfoEnabled()) {
    50                 long elapsedTime = System.currentTimeMillis() - startTime;
    51                 logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
    52             }
    53 
    54             return this.context;
    55         }
    56         catch (RuntimeException ex) {
    57             logger.error("Context initialization failed", ex);
    58             servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
    59             throw ex;
    60         }
    61         catch (Error err) {
    62             logger.error("Context initialization failed", err);
    63             servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
    64             throw err;
    65         }
    66     }

    几个比较重要的步骤是:

    1. 19行this.context = createWebApplicationContext(servletContext);

    初始化1个WebApplicationContext,默认是XmlWebApplicationContext通过BeanUtils.instantiateClass创建的,XmlWebApplicationContext这个类名是写在orgspringframeworkwebcontextContextLoader.properties里的.

    org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

    不喜欢的话自己也可以通过servletContext里去配置contextClass这个参数指定class来配置WebApplicationContext.但是我想一般没人这么做吧.

    2. 32行configureAndRefreshWebApplicationContext(cwac, servletContext);

    初始化wac.大概是最重要的入口了.后面再仔细看.

    3. 35行servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

    如果前面初始化成功的话就把wac绑定到servletContext的attr中(org.springframework.web.context.WebApplicationContext.ROOT),.所以其实我们在servlet环境中如果要获取wac,要么通过ApplicationContextAware要么通过这个servletContext的attr...都是可以的.

    configureAndRefreshWebApplicationContext方法

     1     protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
     2         if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
     3             // The application context id is still set to its original default value
     4             // -> assign a more useful id based on available information
     5             String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
     6             if (idParam != null) {
     7                 wac.setId(idParam);
     8             } else {
     9                 // Generate default id...
    10                 wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
    11                         ObjectUtils.getDisplayString(sc.getContextPath()));
    12             }
    13         }
    14 
    15         wac.setServletContext(sc);
    16         String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
    17         if (configLocationParam != null) {
    18             wac.setConfigLocation(configLocationParam);
    19         }
    20 
    21         // The wac environment's #initPropertySources will be called in any case when the context
    22         // is refreshed; do it eagerly here to ensure servlet property sources are in place for
    23         // use in any post-processing or initialization that occurs below prior to #refresh
    24         ConfigurableEnvironment env = wac.getEnvironment();
    25         if (env instanceof ConfigurableWebEnvironment) {
    26             // 一开始env里的propertySources里面的servletContextInitParams和servletContextConfigParams都是空的,需要用相应的servlet的值去替换
    27             ((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
    28         }
    29 
    30         customizeContext(sc, wac);
    31         wac.refresh();
    32     }

    然后我们来看看这个最重要的初始化wac的方法.

    L15 wac.setServletContext(sc); wac要初始化需要servlet环境相关的数据.

    L16.获取servletContext里配置的contextConfigLocation的值.这个变量大家肯定不陌生.这个就是需要加载的spring配置文件的路径地址.

    L18 contextConfigLocation值设置给wac.所以wac初始化需要spring的配置文件(废话).

    L24-28 让wac的env去加载servlet环境相关的数据.因为之前wac是用beanUtils创建的.创建的时候并不知道你当前的环境有什么变量.所以这里需要加载一下servlet环境的properties.

    initPropertySources有2个参数.第一个servletContext.第二个是servletConfig.很明显.这里是在listener而不是springMVC的dispatcherServlet里,所以这里servletConfig是null.

    L30  customizeContext(sc, wac);在wac启动之前允许你定制一下.

     1     /**
     2      * Customize the {@link ConfigurableWebApplicationContext} created by this
     3      * ContextLoader after config locations have been supplied to the context
     4      * but before the context is <em>refreshed</em>.
     5      * <p>The default implementation {@linkplain #determineContextInitializerClasses(ServletContext)
     6      * determines} what (if any) context initializer classes have been specified through
     7      * {@linkplain #CONTEXT_INITIALIZER_CLASSES_PARAM context init parameters} and
     8      * {@linkplain ApplicationContextInitializer#initialize invokes each} with the
     9      * given web application context.
    10      * <p>Any {@code ApplicationContextInitializers} implementing
    11      * {@link org.springframework.core.Ordered Ordered} or marked with @{@link
    12      * org.springframework.core.annotation.Order Order} will be sorted appropriately.
    13      *
    14      * @param sc  the current servlet context
    15      * @param wac the newly created application context
    16      * @see #CONTEXT_INITIALIZER_CLASSES_PARAM
    17      * @see ApplicationContextInitializer#initialize(ConfigurableApplicationContext)
    18      */
    19     protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {
    20         // 找到 web.xml里配置的 globalInitializerClasses 和 contextInitializerClasses 对应的class
    21         List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
    22                 determineContextInitializerClasses(sc);
    23         // 一般情况下没人配置,所以是empty.
    24         if (initializerClasses.isEmpty()) {
    25             // no ApplicationContextInitializers have been declared -> nothing to do
    26             return;
    27         }
    28 
    29         // 如果这些ApplicationContextInitializer存在的话就调用他们的initialize方法.
    30         ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerInstances =
    31                 new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>();
    32 
    33         for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
    34             Class<?> initializerContextClass =
    35                     GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
    36             if (initializerContextClass != null) {
    37                 Assert.isAssignable(initializerContextClass, wac.getClass(), String.format(
    38                         "Could not add context initializer [%s] since its generic parameter [%s] " +
    39                                 "is not assignable from the type of application context used by this " +
    40                                 "context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(),
    41                         wac.getClass().getName()));
    42             }
    43             initializerInstances.add(BeanUtils.instantiateClass(initializerClass));
    44         }
    45 
    46         AnnotationAwareOrderComparator.sort(initializerInstances);
    47         for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : initializerInstances) {
    48             initializer.initialize(wac);
    49         }
    50     }
    View Code

    可以参考我写的注释.就是允许你在servletContext中配置2个参数 globalInitializerClasses 和 contextInitializerClasses.他们对应的类都是ApplicationContextInitializer的实现.如果你配置了.那这里会调用

    initializer.initialize(wac);

    这个方法执行你的回调函数.

    我记得我第一家公司这里好像加载了一些properties文件..(虽然我现在想想不明白为什么要在这里加载而不是直接配置在spring的配置文件中....或者直接使用@PropertySource或者@ConfigurationProperties)..算是1个spring的扩展点吧.

    L31 前面的配置也做完了..那就真正开始初始化wac了.执行wac.refresh();刷新wac.

    小结

    主要学习了XmlWebApplicationContext刷新前的ContextLoaderListener做的一些预备工作..比如:

    1.默认加载的是哪个wac.你也可以自己定制.

    2.env读取servletContext与Config的参数

    3.自定义的customer,ApplicationContextInitializer

    4.初始化wac成功以后绑定到servletContext的attr中

    等等...

    后续准备研究wac是怎么refresh的.

  • 相关阅读:
    Identity Server4 基础应用(一)Client Credentials
    AX2012 form displays unusually because of native resolution issues(由于本机高分辨率问题导致AX2012界面显示异常)
    AX视图View中添加静态方法
    AX2012导Demo数据
    AX多线程编译
    C# ListView用法详解 很完整
    用 C# 如何判断数据库中是否存在一个值
    ASCII码对照表
    C#中Chart的简单使用(柱状图和折线图)
    使用Filezilla Server配置FTP服务器
  • 原文地址:https://www.cnblogs.com/abcwt112/p/7753477.html
Copyright © 2011-2022 走看看