摘要:
Spring 源码 core 模块关于配置文件的解析与 Bean 的加载研究
spring-core
基本
配置文件: spring-PeppaPig.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="user" class="com.peggy.dao.User"/>
</beans>
启动代码: Test.java
public class Test {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("spring-${username}.xml");
}
}
注入对象: User.java
public class User {
private String id;
private String userName;
}
ClassPathXmlApplicationContext
继承关系 流程图 如下:
启动类:
进入到启动类中,构造方法会一直调用父类构造直到 AbstractApplicationContext
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
//调用父类构造方法,进行相关对象的创建等操作
super(parent);
//设置赔配置资源的路径
setConfigLocations(configLocations);
if (refresh) {
//核心步骤与核心流程
refresh();
}
}
public AbstractXmlApplicationContext(@Nullable ApplicationContext parent) {
super(parent);
}
public AbstractRefreshableConfigApplicationContext(@Nullable ApplicationContext parent) {
super(parent);
}
public AbstractRefreshableApplicationContext(@Nullable ApplicationContext parent) {
super(parent);
}
public AbstractApplicationContext(@Nullable ApplicationContext parent) {
this();
setParent(parent);
}
public AbstractApplicationContext() {
//创建资源模式处理器(用于解析当前系统运行的时候所需要的一系列资源- 写的配置文件 写的 xml 文件)
this.resourcePatternResolver = getResourcePatternResolver();
}
protected ResourcePatternResolver getResourcePatternResolver() {
//创建一个资源模式解析器(其实就是用来解析 xml 配置文件)
return new PathMatchingResourcePatternResolver(this);
}
这里最终调用 getResourcePatternResolver() 方法 ,创建一个用于解析 xml 的资源解析器。
设置配置文件路径
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;
}
}
解析给定路径: resolvePath
protected String resolvePath(String path) {
return getEnvironment().resolveRequiredPlaceholders(path);
}
此方法的作用是将 path 中的变量进行解析,比如将 new ClassPathXmlApplicationContext("spring-${username}.xml"); 中的 ${username} 解析成主机名。例如解析为 spring-peggy.xml
getEnvironment 的方法内部是很简单的,对当前对象的环境进行一个判断如果为空,则通过 createEnvironment() 进行创建一个StandardEnvironment 对象。
protected ConfigurableEnvironment createEnvironment() {
return new StandardEnvironment();
}
Environment 接口体系
路径Placeholder处理
private final ConfigurablePropertyResolver propertyResolver =
new PropertySourcesPropertyResolver(this.propertySources);
@Override
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
// text 文件的配置路径 spring-${username}.xml
return this.propertyResolver.resolveRequiredPlaceholders(text);
}
@Override
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
if (this.strictHelper == null) {
this.strictHelper = createPlaceholderHelper(false);
}
return doResolvePlaceholders(text, this.strictHelper);
}
PropertyResolver接口继承关系
解析
AbstractPropertyResolver.reresolveRequiredPlaceholders
@Override
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
if (this.strictHelper == null) {
this.strictHelper = createPlaceholderHelper(false);
}
return doResolvePlaceholders(text, this.strictHelper);
}
private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolvablePlaceholders) {
return new PropertyPlaceholderHelper(this.placeholderPrefix, this.placeholderSuffix,
this.valueSeparator, ignoreUnresolvablePlaceholders);
}
doResolvePlaceholders
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
return helper.replacePlaceholders(text, this::getPropertyAsRawString);
}
在 spring 源码中,往往以 do * 开头的方法都是具体执行逻辑业务的主要方法,这里的 doReslovePlaceholders 主要完成的就是解析之前在 ClassPathXmlApplicationContext("spring-${username}.xml");中的占位符解析。这里主要由 PropertyPlaceholderHelper.replacePlaceholders 完成。
public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
Assert.notNull(value, "'value' must not be null");
return parseStringValue(value, placeholderResolver, null);
}
可以看到的是,真正的解析还没有开始,主要的解析过程在 PropertyPlaceholderHelper.parseStringValue 中执行。
parseStringValue 占位符解析
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");
}
// 这里再次进行递归的调用解析占位符中的占位符 spring-${ ${username} }.xml
// 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();
}
这里从解析到占位符内容 placeholder = “username ” 由 getPropertyAsRawString 方法处理获取最终的解析结果资源 Property
protected String getPropertyAsRawString(String key) {
return getProperty(key, String.class, false);
}
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
if (this.propertySources != null) {
for (PropertySource<?> propertySource : this.propertySources) {
if (logger.isTraceEnabled()) {
logger.trace("Searching for key '" + key + "' in PropertySource '" +
propertySource.getName() + "'");
}
Object value = propertySource.getProperty(key);
if (value != null) {
if (resolveNestedPlaceholders && value instanceof String) {
value = resolveNestedPlaceholders((String) value);
}
logKeyFound(key, propertySource, value);
return convertValueIfNecessary(value, targetValueType);
}
}
}
if (logger.isTraceEnabled()) {
logger.trace("Could not find key '" + key + "' in any property source");
}
return null;
}
最终获取占位符资源的具体对象内容 value = “peggy” ,最终返回解析后得到的具体 xml 文件名。
refresh 核心步骤与流程
上面解析给定路径资源部分,完成了对于 xml 资源文件的定位,接下来就就是 spring 的核心 bean 的解析与加载,refresh 是也是整个spring 的主要方法,只要搞明白了 refresh 方法,就相当于明白了 spring 的整个 bean 的加载管理的处理流程。
AbstractApplicationContext.refresh
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
/**
* 1、设置容器的启动时间
* 2、设置活跃状态为 true
* 3、设置关闭状态为 false
* 4、获取 Enveronment 对象,并且加载系统的属性值到 Enveronment 对象当中
* 5、准备监听器和事件集合对象,默认为空的集合
*/
//容器刷新前的准备工作(容器刷新的前戏)
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
//创建容器对象: DefaultListableBeanFactory
//加载 xml 配置文件的属性值到当前工厂当中,并且获得 Bean 工厂,最重要的就是 BeanDefaction
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 设置 Bean 工厂的一些初始的属性值( BeanFactory 的前戏)
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();
}
}
}
容器刷新的前置工作 prepareRefresh 方法
protected void prepareRefresh() {
// Switch to active.
// 设置容器的启动时间
this.startupDate = System.currentTimeMillis();
// 设置活跃状态为 true
this.closed.set(false);
// 设置关闭状态为 false
this.active.set(true);
//日志处理
if (logger.isDebugEnabled()) {
if (logger.isTraceEnabled()) {
logger.trace("Refreshing " + this);
}
else {
logger.debug("Refreshing " + getDisplayName());
}
}
// Initialize any placeholder property sources in the context environment.
// 留给子类覆盖,初始化属性资源(内部是空的留给用户自己扩展)
initPropertySources();
// Validate that all properties marked as required are resolvable:
// see ConfigurablePropertyResolver#setRequiredProperties
// 获取 Enveronment 对象,验证需要的属性文件是否已经放入系统当中
getEnvironment().validateRequiredProperties();
// Store pre-refresh ApplicationListeners...
//判断刷新当前的应用程序监听器是否为空,如果为空,则将监听器添加到集合
if (this.earlyApplicationListeners == null) {
// applicationListenters 是留给扩展实现的(SpringBoot 项目当中这里的监听器会在初始化的时候注入)
this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
}
else {
// Reset local application listeners to pre-refresh state.
//如果不等于空,清空监听器集合当中的对象
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
// 创建刷新前的监听事件集合
this.earlyApplicationEvents = new LinkedHashSet<>();
}
获取当前的环境,属性校验
ConfigurableEnvironment.validateRequiredProperties
public ConfigurableEnvironment getEnvironment() {
if (this.environment == null) {
this.environment = createEnvironment();
}
return this.environment;
}
requiredProperties是通过setRequiredProperties方法设置的,保存在一个list里面,默认是空的,也就是不需要校验任何属性。
BeanFactory 创建
BeanFactory 创建
beanFactory 的创建由调用 AbstractApplicationContext.obtainFreshBeanFactory 实现。
//加载 xml 配置文件的属性值到当前工厂当中,并且获得 Bean 工厂,最重要的就是 BeanDefaction
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//初始化 BeanFactory, 并进行 xml 文件的读取,并将得到的 BeanFactory 记录在当前的实体的属性中(可能在之前是存在 BeanFactroy 工厂的)
refreshBeanFactory();
return getBeanFactory();
}
protected final void refreshBeanFactory() throws BeansException {
//判断工厂是否存在
if (hasBeanFactory()) {
//销毁之前的工厂
destroyBeans();
//关闭工厂
closeBeanFactory();
}
try {
//创建一个新的 DefaultListableBeanFactory 对象
DefaultListableBeanFactory beanFactory = createBeanFactory();
// 1. 设置属性值(为了序列化指定 id 可以从 id 反序列化到 BeanFactory 对象)
beanFactory.setSerializationId(getId());
// 2. 加载 xml 文件的一些配置(定制 beanFactory 设置相关属性, 包括是否允许覆盖同名称的不同定义的对象以及循环依赖)
customizeBeanFactory(beanFactory);
// 3. 初始化 domcumentReader , 并进行 xml 文件读取解析
loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
BeanFactory 接口
此接口实际上就是一个 Bean 容器,其继承关系的体系:
创建 DefaultListableBeanFactory 对象
protected DefaultListableBeanFactory createBeanFactory() {
return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}
AbstartApplicationContext.getInternalParentBeanFactory
从内部获取一个 BeanFactory 对象,这里的 BeanFactory 是为初始化不存在的为 Null
protected BeanFactory getInternalParentBeanFactory() {
return (getParent() instanceof ConfigurableApplicationContext ?
((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent());
}
创建 DefalultListableBeanFactory 对象,调用父类构造
public DefaultListableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
super(parentBeanFactory);
}
创建 AbstractAutwireCapableBeanFactory
public AbstractAutowireCapableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
this();
setParentBeanFactory(parentBeanFactory);
}
父类构造调用无参构造,设置需要忽略依赖接口
public AbstractAutowireCapableBeanFactory() {
super();
//忽略要依赖的接口
ignoreDependencyInterface(BeanNameAware.class);
ignoreDependencyInterface(BeanFactoryAware.class);
ignoreDependencyInterface(BeanClassLoaderAware.class);
}
在 ignoreDependencyInterface 当中设置的接口都是都是以 * Aware 结尾,在首页的关于 ClassPathApplicationContext 中的 UML 关系图中有提到过关于 << Aware >> 接口。
AbstractAutwireCapableBeanFactory.ignoreDependencyInterface 方法实际上是将这些类的对象添加到了集合当中,这也印证了AbstractAutwireCapableBeanFactory 的子类DefalultListableBeanFactory为什么是 Listable 其对象本身就是一个大的容器盒。
关于该接口的作用官方的解释如下:
额~~~ 这一块我也没搞懂,后续有新的了解再做其补充
A marker superinterface indicating that a bean is eligible to be notified by the Spring container of a particular framework object through a callback-style method. The actual method signature is determined by individual subinterfaces but should typically consist of just one void-returning method that accepts a single argument.
AbstractAutowireCapableBeanFactory.setParentBeanFactory
@Override
public void setParentBeanFactory(@Nullable BeanFactory parentBeanFactory) {
if (this.parentBeanFactory != null && this.parentBeanFactory != parentBeanFactory) {
throw new IllegalStateException("Already associated with parent BeanFactory: " + this.parentBeanFactory);
}
if (this == parentBeanFactory) {
throw new IllegalStateException("Cannot set parent bean factory to self");
}
this.parentBeanFactory = parentBeanFactory;
}
由于之前从 AbstartApplicationContext.getInternalParentBeanFactory 获取的内部 BeanFactory 是空的,因此这里传入的 parentBeanFactory 是一个空父类 Bean 工厂对象。
直接返回一个新的对象 DefaultListableBeanFactory 对象。关于父类的 DefaltListableBeanFactory 的父类 AbstractAutowireCapableBeanFactory 中的 BeanFactory 对象 ParentBeanFactory 对象具体是用来干什么的,为什么需要在创建 DefaultListableBeanFactory 对象的时候,需要从内部获取一次 parentBeanFactory ,这里一个可以需要后续的知识储备才能解答叭。
为 beanFactory 设置序列化 ID
// 1. 设置属性值(为了序列化指定 id 可以从 id 反序列化到 BeanFactory 对象)
beanFactory.setSerializationId(getId());
@Override
public String getId() {
return this.id;
}
/**
* Specify an id for serialization purposes, allowing this BeanFactory to be
* deserialized from this id back into the BeanFactory object, if needed.
*/
public void setSerializationId(@Nullable String serializationId) {
if (serializationId != null) {
serializableFactories.put(serializationId, new WeakReference<>(this));
}
else if (this.serializationId != null) {
serializableFactories.remove(this.serializationId);
}
this.serializationId = serializationId;
}
AbstractApplicationContext.getId
//创建上下文的唯一标识
/** Unique id for this context, if any. */
private String id = ObjectUtils.identityToString(this);
这里的 this.id 的初始化时机在 AbstractApplicationContext 被创建的时候所复制得到的,而 AbstractApplicationContext 对象的创建时机在 ClassPathXmlApplicationContext 构造方法中多次调用父类构造完成初始化。
BeanFactory 的定制
在 AbstractRefreshableApplicationContext.customizeBeanFactory 的方法当中为我们自己继承的 MyCalssPathXmlApplication 中通过对继承的 ClassPathXmlApplication 类中的 customizeBeanFactory(DefaultListableBeanFactory beanFactory)方法进行重写。
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
//如果属性 allowBeanDefinitionOverrding 不为空, 设置 beanFactory 对象相应的属性值,是否允许覆盖同名称的不同定义的对象
/**
* 这里的 allowBeanDefintionOverriding 与 allowCircularReferences 默认在父类的构造方法中赋值为 true
* 而这里的值,我们可以在自己定义继承的类 MyClassPathXmlApplication 中通过对继承的 ClassPathXmlApplication 类中的
* customizeBeanFactory(DefaultListableBeanFactory beanFactory) 方法进行重写
*/
if (this.allowBeanDefinitionOverriding != null) {
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.allowCircularReferences != null) {
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
}
自定义实现 customizeBeanFactory 方法
public class MyClassPathXmlApplicationContext(){
@Override
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
super.customizeBeanFactory(beanFactory);
System.out.println("调用了自定义的方法")
}
}
public class Test{
public static void main(String[] args){
ApplicationContext context=new MyClassPathXmlApplicationContext("spring-${username}.xml");
}
}
解析 xml 初始化 domcumentReader
AbstractXmlApplicationContext.loadBeanDefinitions 该方法当中就完成了,xml 文件的解析工作,以及对于 bean 的初始化加载。
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
// 创建一个 xml 的 beanDefinitionReader , 并通过回调设置到 BeanFactory 中
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
// 给 reader 对象设置环境对象
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
// 初始化 beanDefinitionReader 对象 , 此处设置配置文件是否要进行验证
initBeanDefinitionReader(beanDefinitionReader);
// 开始完成 beanDefinition 的加载
loadBeanDefinitions(beanDefinitionReader);
}
AbstractXmlApplcationContext.loaderBeanDefinitons
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
//以为 Resource[] 数组形式的配置文件路径
//ClassPathXmlApplicationContext(String[] paths, Class<?> clazz, @Nullable ApplicationContext parent)
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
//以为字符串数组形式的配置文件路径
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
protected String[] getConfigLocations() {
return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations());
}
AbstractBeanDefactionReader.loadBeanDefinitions
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
int count = 0;
for (String location : locations) {
count += loadBeanDefinitions(location);
}
return count;
}
@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
// 此处获取 resourceLoader 对象
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
}
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
//调用 DefinitionResourxesLoader 的 getResources 完成具体的 Resource 的定位
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int count = loadBeanDefinitions(resources);
if (actualResources != null) {
Collections.addAll(actualResources, resources);
}
if (logger.isTraceEnabled()) {
logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
}
return count;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
// Can only load single resources by absolute URL.
Resource resource = resourceLoader.getResource(location);
int count = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isTraceEnabled()) {
logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
}
return count;
}
}