一、注意事项:
本次源码解读基于Spring-Framework 5.2.9版本,可自行通过官网下载源码,本地安装好gradle后可自行编译和运行,跟随本教程走进Spring底层
二、spring创建bean的整体流程
三、查看入口代码
通过main方法自定义测试类
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
Person bean = ac.getBean(Person.class);
Person bean2 = ac.getBean(Person.class);
四、入口代码解析
通过new ClassPathXmlApplicationContext(“applicationContext.xml”)查看spring源码,调用的如下方法:
/**
*
* 通过给定的parent创建一个ClassPathXmlApplicationContext对象,将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);
// 将给定的xml文件转成string(支持spEL表达式的解析)对象,保存在configLocations中
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
五、super(parent)源码跟踪
我们知道java中子类的创建必须先调用父类的构造方法,我们不断向上跟进,看看spring做了哪些事情
AbstractApplicationContext
跟进到父类AbstractApplicationContext中发现,该有参构造中主要做了两件事情
/**
* 主要做两件事情 1 调用无参构造创建资源模式处理器PathMatchingResourcePatternResolver
* 2 设置parent,此处为null,在springMVC中有扩展
* Create a new AbstractApplicationContext with the given parent context.
* @param parent the parent context
*/
public AbstractApplicationContext(@Nullable ApplicationContext parent) {
this();
setParent(parent);
}
/**
* 创建一个无父类的AbstractApplicationContext对象
* Create a new AbstractApplicationContext with no parent.
*/
public AbstractApplicationContext() {
// 创建资源模式处理器
this.resourcePatternResolver = getResourcePatternResolver();
}
protected ResourcePatternResolver getResourcePatternResolver() {
// 创建一个资源模式解析器(其实就是用来解析xml配置文件)
return new PathMatchingResourcePatternResolver(this);
}
DefaultResourceLoader
从AbstractApplicationContext继续向上跟进,进入DefaultResourceLoader的无参构造
/**
* 基于当前线程初始化类加载器
*/
public DefaultResourceLoader() {
this.classLoader = ClassUtils.getDefaultClassLoader();
}
@Nullable
public static ClassLoader getDefaultClassLoader() {
// 定义类加载器变量c1
ClassLoader cl = null;
try {
// 获取当前线程的上下文类加载器
cl = Thread.currentThread().getContextClassLoader();
}
catch (Throwable ex) {
// Cannot access thread context ClassLoader - falling back...
}
// 如果cl为null
if (cl == null) {
// No thread context class loader -> use class loader of this class.
// 没有线程上下文类加载器 -> 使用这个类的类加载器
// 获取ClassUtils的类加载器
cl = ClassUtils.class.getClassLoader();
// 如果c1还为null
if (cl == null) {
// getClassLoader() returning null indicates the bootstrap ClassLoader
// getClassLoader() 返回null表示boostrap ClassLoader
try {
// 获取系统的类加载器
cl = ClassLoader.getSystemClassLoader();
}
catch (Throwable ex) {
// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
}
}
}
return cl;
}
六、setConfigLocations(configLocations)源码跟踪
本方法主要功能在于将给定的xml文件转成string(支持spEL表达式的解析)对象,保存在configLocations中。其中有几个关键点需要注意
初始化环境对象
追踪该方法,发现调用了org.springframework.context.support.AbstractRefreshableConfigApplicationContext#resolvePath
/**
* 解析给定的xml路径,并将路径中的占位符使用环境变量替代
*/
protected String resolvePath(String path) {
return getEnvironment().resolveRequiredPlaceholders(path);
}
/**
* 查询Environment对象,如果有,则返回,如果没有,则创建(这里我们是第一次进来,environment为null,需要创建)
* 支持用户通过实现类,进行自定义扩展
* Return the {@code Environment} for this application context in configurable
* form, allowing for further customization.
* <p>If none specified, a default environment will be initialized via
* {@link #createEnvironment()}.
*/
@Override
public ConfigurableEnvironment getEnvironment() {
if (this.environment == null) {
this.environment = createEnvironment();
}
return this.environment;
}
/**
* 系统默认创建并返回一个StandardEnvironment对象
* 支持用户通过实现类,进行自定义扩展
* Create and return a new {@link StandardEnvironment}.
* <p>Subclasses may override this method in order to supply
* a custom {@link ConfigurableEnvironment} implementation.
*/
protected ConfigurableEnvironment createEnvironment() {
return new StandardEnvironment();
}
跟进StandardEnvironment的父类无参构造,查看
public AbstractEnvironment() {
customizePropertySources(this.propertySources);
}
发现调用了子类的customizePropertySources方法,我们这里是调用的StandardEnvironment的
/**
* 将系统中Java环境的属性资源添加到MutablePropertySources中
*/
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(
new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(
new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
自此,完成了环境对象StandardEnvironment的创建,并将系统资源保存在变量MutablePropertySources中,方便后续直接使用,下面是StandardEnvironment的类图
解析文件路径中的占位符
将上图中的getEnvironment().resolveRequiredPlaceholders(path)方法继续追踪resolveRequiredPlaceholders(path)方法,确认最终核心实现逻辑org.springframework.util.PropertyPlaceholderHelper#parseStringValue
/**
* 以递归的方式解析xml路径,进行占位符的解析,最终返回解析后的完整路径
*
*/
protected String parseStringValue(
String value, PlaceholderResolver placeholderResolver, @Nullable Set<String> visitedPlaceholders) {
int startIndex = value.indexOf(this.placeholderPrefix);
// 不包含占位符,则直接返回
if (startIndex == -1) {
return value;
}
StringBuilder result = new StringBuilder(value);
// 包含占位符,则进行解析,使用环境变量中的属性对占位符进行替代
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 == null) {
visitedPlaceholders = new HashSet<>(4);
}
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();
}
至此,spring工厂的准备工作完成,接下来将进入解析xml至bean初始化完成的核心流程。