Spring Framework--ApplicationComtext(2)以ClassPathXmlApplicationContext看ApplicationContext

来源:互联网 发布:软件测试工程师的出路 编辑:程序博客网 时间:2024/06/05 23:05

前言

​ 上篇博客我们简单介绍了ApplicationContext,说实话,讲得太糙了,自己都看不下去了。所以打算在本文和后面的文章以稍微详细的说明来弥补之前的不足。本文将以debug ClassPathXmlApplicationContext的方式一步一步去了解Application。

1. 概述

首先让我们来看个spring的测试用例:

    private static final String PATH = "/org/springframework/context/support/";    private static final String CONTEXT_A = "test/contextA.xml";    private static final String CONTEXT_B = "test/contextB.xml";    private static final String CONTEXT_C = "test/contextC.xml";    private static final String FQ_CONTEXT_A = PATH + CONTEXT_A;    private static final String FQ_CONTEXT_B = PATH + CONTEXT_B;    private static final String FQ_CONTEXT_C = PATH + CONTEXT_C;@Test    public void testMultipleConfigLocations() {      // 根据多个xml路径实例化beans        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(                FQ_CONTEXT_B, FQ_CONTEXT_C, FQ_CONTEXT_A);      // 断言,BeanFacoty(containsBean是ClassPathXmlApplication的纠结父类BeanFantocty的方法)里面是否包含传入参数的方法。        assertTrue(ctx.containsBean("service"));        assertTrue(ctx.containsBean("logicOne"));        assertTrue(ctx.containsBean("logicTwo"));      // 通过getBean获取相应的实例化bean        // re-refresh (after construction refresh)        Service service = (Service) ctx.getBean("service");        ctx.refresh();        assertTrue(service.isProperlyDestroyed());        // regular close call        service = (Service) ctx.getBean("service");        ctx.close();        assertTrue(service.isProperlyDestroyed());        // re-activating and re-closing the context (SPR-13425)        ctx.refresh();        service = (Service) ctx.getBean("service");        ctx.close();        assertTrue(service.isProperlyDestroyed());    }

上面是一个ClassPathXmlApplicationContext的应用,通过xml的路径,获取xml下的bean配置,从而获取实例化bean。

1.1 初识ClassPathXmlApplicationContext

下面看看Spring中开发者对ClassPathXmlApplicationContext的讲解

/** * Standalone XML application context, taking the context definition files * from the class path, interpreting plain paths as class path resource names * that include the package path (e.g. "mypackage/myresource.txt"). Useful for * test harnesses as well as for application contexts embedded within JARs. * 独立XML应用程序上下文,从类路径中获取上下文定义文件,将纯路径解释为包含程序包路径的类路径资源名称(例如“mypackage / myresource.txt”)。对测试工具以及嵌入JAR的应用程序上下文非常有用。 * <p>The config location defaults can be overridden via {@link #getConfigLocations}, * Config locations can either denote concrete files like "/myfiles/context.xml" * or Ant-style patterns like "/myfiles/*-context.xml" (see the * {@link org.springframework.util.AntPathMatcher} javadoc for pattern details). * * <p>Note: In case of multiple config locations, later bean definitions will * override ones defined in earlier loaded files. This can be leveraged to * deliberately override certain bean definitions via an extra XML file. * * <p><b>This is a simple, one-stop shop convenience ApplicationContext. * Consider using the {@link GenericApplicationContext} class in combination * with an {@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader} * for more flexible context setup.</b> * * @author Rod Johnson * @author Juergen Hoeller * @see #getResource * @see #getResourceByPath * @see GenericApplicationContext */

下面是ClassPathXmlApplicationContext类图!这里写图片描述

由图可知,ClassPathXmlApplicationContext继承关系为:

@startuml

ApplicationContext<|—AbstactApplicationContext<|—AbstractRefreshableApplicationContext<—AbstractRefreshableConfigApplicationContext<|—AbstractXmlApplicationContext<|–ClassPathXmlApplicationContext

@enduml

下图为ClassPathXmlApplicationContext所包含的方法:

这里写图片描述

又上图可知,ClassPathXmlApplicationContext几乎全是构造方法的重载(Overload)

/**     *   不难看出,其造函数主要分为两类:    *       1.指定xml文件配置路径,不指定需要获取的bean实例化对象。    *       2.指定xml文件配置路径,指定需要获取的bean实例化对象。    *  分别为一下两种方法。    *//**     * Create a new ClassPathXmlApplicationContext with the given parent,     * loading the definitions from the given XML files.     * @param configLocations array of resource locations     * @param refresh whether to automatically refresh the context,     * loading all bean definitions and creating all singletons.     * Alternatively, call refresh manually after further configuring the context.     * @param parent the parent context     * @throws BeansException if context creation failed     * @see #refresh()     */public ClassPathXmlApplicationContext(        String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)            throws BeansException {        super(parent);        setConfigLocations(configLocations);        if (refresh) {            refresh();        }    }/**     * Create a new ClassPathXmlApplicationContext with the given parent,     * loading the definitions from the given XML files and automatically     * refreshing the context.     * @param paths array of relative (or absolute) paths within the class path     * @param clazz the class to load resources with (basis for the given paths)     * @param parent the parent context     * @throws BeansException if context creation failed     * @see org.springframework.core.io.ClassPathResource#ClassPathResource(String, Class)     * @see org.springframework.context.support.GenericApplicationContext     * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader     */public ClassPathXmlApplicationContext(String[] paths, Class<?> clazz, @Nullable ApplicationContext parent)    throws BeansException {        super(parent);        Assert.notNull(paths, "Path array must not be null");        Assert.notNull(clazz, "Class argument must not be null");        this.configResources = new Resource[paths.length];        for (int i = 0; i < paths.length; i++) {            this.configResources[i] = new ClassPathResource(paths[i], clazz);        }        refresh();    }

2. 一起学习源码—debug spring test

​ 下面将debug org.springframework.context.support.ClassPathXmlApplicationContextTests 中的testConfigLocationPattern,以此了解ApplicationContext中的ClassPathXmlApplicationContext的初始化过程。

private static final String PATH = "/org/springframework/context/support/";private static final String CONTEXT_WILDCARD = PATH + "test/context*.xml";@Test    public void testConfigLocationPattern() {        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(CONTEXT_WILDCARD);        assertTrue(ctx.containsBean("service"));        assertTrue(ctx.containsBean("logicOne"));        assertTrue(ctx.containsBean("logicTwo"));        Service service = (Service) ctx.getBean("service");        ctx.close();        assertTrue(service.isProperlyDestroyed());    }

可知,此处是一个通过通配符获取test目录下所有已context打头的所有xml配置文件,以此初始化ClassPathXmlApplicationContext,从而实例化beans。

以下一段只是觉得搞笑,当做注释吧!

哈哈,太好玩了。当我进入到

ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(CONTEXT_WILDCARD);

这个函数时,没想到第一步居然是跳到了AbstractApplicationContext下的一段静态代码块

static {        // Eagerly load the ContextClosedEvent class to avoid weird classloader issues        // on application shutdown in WebLogic 8.1. (Reported by Dustin Woods.)        ContextClosedEvent.class.getName();    }

注意看注释,他说,这么急切的加载ContextClosedEvent类,是为了避免WebLogic 8.1中在关闭应用程序的时候出现奇怪的类加载器问题。

~QAQ~ 莫名的喜感

然后进入到ClassPathXmlApplicationContext的构造函数中,最终会调用以下这个构造函数

public ClassPathXmlApplicationContext(                    String[] configLocations,                    boolean refresh,                     @Nullable ApplicationContext parent)throws BeansException {        super(parent);        setConfigLocations(configLocations);        if (refresh) {            refresh();        }    }

(1)Super(parent)—>首先会去加载父类的构造方法;

org/springframework/context/support/AbstractApplicationContext.java/**     * Create a new AbstractApplicationContext with the given parent context.     * @param parent the parent context     */public AbstractApplicationContext(@Nullable ApplicationContext parent) {        this();        setParent(parent);    }    //---------------------------------------------------------------------    // Implementation of ConfigurableApplicationContext interface    //---------------------------------------------------------------------    /**     * Set the parent of this application context.     * <p>The parent {@linkplain ApplicationContext#getEnvironment() environment} is     * {@linkplain ConfigurableEnvironment#merge(ConfigurableEnvironment) merged} with     * this (child) application context environment if the parent is non-{@code null} and     * its environment is an instance of {@link ConfigurableEnvironment}.     * @see ConfigurableEnvironment#merge(ConfigurableEnvironment)     */    @Override    public void setParent(@Nullable ApplicationContext parent) {        this.parent = parent;        if (parent != null) {            Environment parentEnvironment = parent.getEnvironment();            if (parentEnvironment instanceof ConfigurableEnvironment) {                getEnvironment().merge((ConfigurableEnvironment) parentEnvironment);            }        }    }

已知这里通过传进来的ApplicationContext获取配置环境,但是显然,我们调用的ClassPathXmlApplicationContext的构造方法传入参数为null。

(2) setConfigLocations(configLocations);——设置spring的配置文件

public void setConfigLocations(@Nullable String... locations) {        if (locations != null) {            Assert.noNullElements(locations, "Config locations must not be null");            this.configLocations = new String[locations.length];            for (int i = 0; i < locations.length; i++) {                this.configLocations[i] = resolvePath(locations[i]).trim();            }        }        else {            this.configLocations = null;        }    }

易知,这里根据传入的xml路径,去解析beans路径。

这里通过resolvePath去获取一个StandardEnvironment环境的。这里值得一提的是对通配符*的解析:

org.springframework.util.PropertyPlaceholderHelperprotected String parseStringValue(String value,                                  PlaceholderResolver placeholderResolver,                                  Set<String> visitedPlaceholders) {        StringBuilder result = new StringBuilder(value);        int startIndex = value.indexOf(this.placeholderPrefix);        while (startIndex != -1) {            int endIndex = findPlaceholderEndIndex(result, startIndex);            if (endIndex != -1) {                String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);                String originalPlaceholder = placeholder;                if (!visitedPlaceholders.add(originalPlaceholder)) {                    throw new IllegalArgumentException(                            "Circular placeholder reference '" + originalPlaceholder + "' in property definitions");                }                // Recursive invocation, parsing placeholders contained in the placeholder key.                placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);                // Now obtain the value for the fully resolved key...                String propVal = placeholderResolver.resolvePlaceholder(placeholder);                if (propVal == null && this.valueSeparator != null) {                    int separatorIndex = placeholder.indexOf(this.valueSeparator);                    if (separatorIndex != -1) {                        String actualPlaceholder = placeholder.substring(0, separatorIndex);                        String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());                        propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);                        if (propVal == null) {                            propVal = defaultValue;                        }                    }                }                if (propVal != null) {                    // Recursive invocation, parsing placeholders contained in the                    // previously resolved placeholder value.                    propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);                    result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);                    if (logger.isTraceEnabled()) {                        logger.trace("Resolved placeholder '" + placeholder + "'");                    }                    startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());                }                else if (this.ignoreUnresolvablePlaceholders) {                    // Proceed with unprocessed value.                    startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());                }                else {                    throw new IllegalArgumentException("Could not resolve placeholder '" +                            placeholder + "'" + " in value \"" + value + "\"");                }                visitedPlaceholders.remove(originalPlaceholder);            }            else {                startIndex = -1;            }        }        return result.toString();    }

最后调用的是org.springframework.util.PropertyPlaceholderHelper 里的parseStringValue方法去解析通配符,其时序图如下附图1。这个后期认真学习。

(3)refresh();——//调用父类的refresh函数,进行一系列初始化

/**     * Load or refresh the persistent representation of the configuration,     * which might an XML file, properties file, or relational database schema.     * <p>As this is a startup method, it should destroy already created singletons     * if it fails, to avoid dangling resources. In other words, after invocation     * of that method, either all or no singletons at all should be instantiated.     * @throws BeansException if the bean factory could not be initialized     * @throws IllegalStateException if already initialized and multiple refresh     * attempts are not supported     */@Override    public void refresh() throws BeansException, IllegalStateException {        synchronized (this.startupShutdownMonitor) {            // Prepare this context for refreshing.            prepareRefresh();            // Tell the subclass to refresh the internal bean factory.            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();            // Prepare the bean factory for use in this context.            prepareBeanFactory(beanFactory);            try {                // Allows post-processing of the bean factory in context subclasses.                postProcessBeanFactory(beanFactory);                // Invoke factory processors registered as beans in the context.                invokeBeanFactoryPostProcessors(beanFactory);                // Register bean processors that intercept bean creation.                registerBeanPostProcessors(beanFactory);                // Initialize message source for this context.                initMessageSource();                // Initialize event multicaster for this context.                initApplicationEventMulticaster();                // Initialize other special beans in specific context subclasses.                onRefresh();                // Check for listener beans and register them.                registerListeners();                // Instantiate all remaining (non-lazy-init) singletons.                finishBeanFactoryInitialization(beanFactory);                // Last step: publish corresponding event.                finishRefresh();            }            catch (BeansException ex) {                if (logger.isWarnEnabled()) {                    logger.warn("Exception encountered during context initialization - " +                            "cancelling refresh attempt: " + ex);                }                // Destroy already created singletons to avoid dangling resources.                destroyBeans();                // Reset 'active' flag.                cancelRefresh(ex);                // Propagate exception to caller.                throw ex;            }            finally {                // Reset common introspection caches in Spring's core, since we                // might not ever need metadata for singleton beans anymore...                resetCommonCaches();            }        }    }

关于refresh函数里面的一些具体函数,后面的博客将逐一介绍。

注:

  • parseStringValue 转义通配符后期需要好好学习。
  • refresh函数后期介绍

附图一:

这里写图片描述

阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 大男当婚原著 岳绮罗原著身世 魔道祖师原著免费阅读 魔道祖师原著 墨菲定律原著 少年派原著结局 原著钟小艾父亲级别 风云原著实力排行 朝花夕拾原著在线阅读 在远方原著在线阅读 原著岳绮罗结局 北京西宸原著 龙湖西宸原著 亲爱的热爱的原著 我在未来等你原著结局 成都龙湖西宸原著 阿米巴原虫 疟原虫 生物原虫全集 间日疟原虫 疟原虫图片 疟原虫四种类型图片 血液原虫病 疟原虫是什么生物 蛛丝马迹 地蛛 蜡皮蜥 鬣蜥 蜥鳄 草蜥 海鬣蜥 红眼鹰蜥 脆蛇蜥 水蜥 帝王蛇蜥 蟾头蜥 变色树蜥 魔蜥 蓝鬣蜥