2、Spring源码学习:认识加载 xml 文件的 ClassPathXmlApplicationContext

版权声明:欢迎转载交流,声明出处即可。体能状态先于精神状态,习惯先于决心,聚焦先于喜好 ——Bestcxx https://blog.csdn.net/bestcxx/article/details/90416266

前言

体能状态先于精神状态,习惯先于决心,聚焦先于喜好

这是一篇很水的文章

因为目前了解还不多,只是按图索骥,画了一下通过XML 初始化 Spring 容器的大体的 UML 类关系图

入口

下面这行代码你一定很熟悉,这是加载 Spring xml 文件的常规操作
ApplicationContext 是接口,ClassPathXmlApplicationContext 是集大成者,也是ApplicationContext 的实现类

ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
从 ClassPathXmlApplicationContext 开始的 UML 图-关注 ApplicationContext

重点关注 ApplicationContext ,可以看到它是 Spring 基本功能定义的集大成者

在这里插入图片描述

特性

ClassPathXmlApplicationContext 的特性

以下内容来自于 spring-context-4.3.0-RELEASE.jar org.springframework.context.support.ClassPathXmlApplicationContext

  • 配置文件路径可以是具体地址"/myfiles/context.xml",或者 Ant-style 风格的地址 “/myfiles/*-context.xml”
  • 如果有多个配置路径,后面定义的 bean 会覆盖重写前面文件已经定义的bean.这个特性可以用于提供单独的xml文件对特殊的 bean 进行定义.
  • 加载Spring 配置文件可以使用 org.springframework.context.support.GenericApplicationContext ,从而可以获得更为灵活的配置
  • 和 ClassPathXmlApplicationContext 有类似作用的类还有org.springframework.context.support.FileSystemXmlApplicationContext、org.springframework.web.context.support.XmlWebApplicationContext、
    org.springframework.web.context.support.AbstractRefreshableWebApplicationContext、
    org.springframework.context.annotation.AnnotationConfigApplicationContext,其中AnnotationConfigApplicationContext支持使用注解 @Configuration 将类作为bean 的定义来源
    在这里插入图片描述

源码

比对 ClassPathXmlApplicationContext 和 FileSystemXmlApplicationContext
/**ClassPathXmlApplicationContext*/
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
/**FileSystemXmlApplicationContext*/
AbstractApplicationContext ac2 = new FileSystemXmlApplicationContext("spring.xml");		
ClassPathXmlApplicationContext 的构造方法内部逻辑

进入源码可以发现 FileSystemXmlApplicationContext 内部逻辑类似
事实上,不管加载来自哪里,初始化 Spring 容器都需要调用

/**
* Create a new ClassPathXmlApplicationContext, loading the definitions
 * from the given XML file and automatically refreshing the context.
 * @param configLocation resource location
 * @throws BeansException if context creation failed
 */
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
		//注意这里为true
		this(new String[] {configLocation}, true, null);
	}
/**
* 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, ApplicationContext parent)
		throws BeansException {
	
	super(parent);
	//将配置文件地址放入数组 
	//AbstractRefreshableConfigApplicationContext.configLocations
	setConfigLocations(configLocations);
	//本场景为true
	if (refresh) {
		//AbstractApplicationContext.refresh(),加载配置文件或刷新配置文件,
		//所有的(non-lazy-init)单例bean要么全部成功,要么全部失败
		refresh();
	}
}
setConfigLocations(configLocations);

将配置文件路径配置放到 数组 configLocations[] 中
因为配置文件可以是多个

/**
 * Set the config locations for this application context.
 * <p>If not set, the implementation may use a default as appropriate.
 */
public void setConfigLocations(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;
	}
}
ConfigurableApplicationContext.refresh() 的接口定义

org.springframework.context.ConfigurableApplicationContext.refresh() 的接口方法定义
它的实现位于 org.springframework.context.support.AbstractApplicationContext.refresh()
事实上,所有初始化 Spring 容器的操作都会调用这个 refresh() 方法,下一篇文章我们将详细讲解
用于加载或者刷新来自 XML 文件、properties 文件 或者关系数据库的配置
是一个在开始阶段调用的方法,简单说,其负责加载的(non-lazy-init)单例 bean 要么全部成功,要么全部失败

/**
* 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
 */
void refresh() throws BeansException, IllegalStateException;

小饼干:eager class

为了加快加载速度,Spring 允许在整个 jar 文件没有全部加载完毕,而只要其中一个类被调用就将其这个类加载,这个被成为 eager class
An alternative approach would be eager class loading - to load classes into the JVM as soon as they arrive (i.e., invoke java.lang.Classloader.defineClass(…)), without buffering them or waiting for the entire archive to arrive. This allows us quicker access to some of the class files in the archive, and eliminates the need to cache or buffer a copy of the jar file.
http://www.cs.umd.edu/~pugh/java/latex2html/pack/node11.html

默认情况下, allowEagerClassLoading=true,并且对于 “lazy-init” 的bean 同样有效
org.springframework.beans.factory.support.DefaultListableBeanFactory.java

/** Whether to allow eager class loading even for lazy-init beans */
	private boolean allowEagerClassLoading = true;
展开阅读全文

没有更多推荐了,返回首页