文章目录
- 前言
- 1. prepareRefresh()
- 2. obtainFreshBeanFactory()
- `loadBeanDefinitions(beanFactory)`
- `loadBeanDefinitions(beanDefinitionReader)`
- `reader.loadBeanDefinitions(configLocations)`
- `loadBeanDefinitions(location)`
- `loadBeanDefinitions(resources)`
- `doLoadBeanDefinitions(inputSource, resource())`
- `registerBeanDefinitions(doc, createReaderContext(resource))`
- `parseBeanDefinitions(root, this.delegate)`
- `processBeanDefinition(ele, delegate)`
- `parseBeanDefinitionElement(ele)`
- `registerBeanDefinition(bdHolder, getReaderContext().getRegistry())`
- 总结
源码阅读传送门
前言
上一章我们了解了IOC容器的相关概念、如何通过代码实例化容器以及容器初始化的核心方法refresh()
,本章开始初探refresh()
,在阅读源码前,我们有必要先了解什么是BeanFactory
以及BeanDefinition
,这将有利于我们对接来下源码的理解。
BeanFactory
BeanFactory顾名思义,Bean的工厂,用于创建与管理Bean。
从上图我们可以看到ApplicationContext
继承了两个BeanFactory
的子接口:
ListableBeanFactory
:listable(可列举的),BeanFactory
只能获取单个Bean,ListableBeanFactory
可以获取多个Bean。HierarchicalBeanFactory
:hierarchical(分层),可以通过配置的方式设置父类的bean工厂。
BeanDefinition
Spring IOC容器管理一个或多个Bean,这些bean使用提供给容器的配置元数据创建的(例如通过xml定义的形式)。在容器本身,这些Bean表示为
BeanDefinition
对象。
BeanDefinition
主要包含哪些信息呢?
- 包限定名,通常是定义bean的具体实现类。
- bean行为配置元素,比如:范围、生命周期回调等。
- 对bean执行工作所需的其它bean引用(依赖)。
- 要在新创建对象中设置的其它配置,比如数据库连接池bean的连接数大小等。
源码如下:
/**
* BeanDefinition用于描述bean实例具有的属性值、构造函数参数值和具体实现等
*/
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
/**
* 单例作用域的作用域标识符
*/
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
/**
* 原型范围的范围标识符
*/
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
/**
* 角色分类-应用
*/
int ROLE_APPLICATION = 0;
/**
* 角色分类-支持
*/
int ROLE_SUPPORT = 1;
/**
* 角色分类-后台
*/
int ROLE_INFRASTRUCTURE = 2;
// Modifiable attributes
/**
* 设置该bean的父名称
*/
void setParentName(@Nullable String parentName);
/**
* 获取···
*/
@Nullable
String getParentName();
/**
* 为该bean指定bean类名
*/
void setBeanClassName(@Nullable String beanClassName);
/**
* 获取···
*/
@Nullable
String getBeanClassName();
/**
* 设置作用域
* @see #SCOPE_SINGLETON
* @see #SCOPE_PROTOTYPE
*/
void setScope(@Nullable String scope);
/**
* 获取作用域
*/
@Nullable
String getScope();
/**
* 设置是否懒加载
*/
void setLazyInit(boolean lazyInit);
/**
* 判断是否懒加载
*/
boolean isLazyInit();
/**
* 设置此bean初始化所依赖的bean名称,
* bean工厂将保证首先初始化这些bean
*/
void setDependsOn(@Nullable String... dependsOn);
/**
* 获取依赖的bean名称数组
*/
@Nullable
String[] getDependsOn();
/**
* 设置该bean是否可以自动注入
*/
void setAutowireCandidate(boolean autowireCandidate);
/**
* 返回···
*/
boolean isAutowireCandidate();
/**
* 设置该bean是否为主要候选bean
*/
void setPrimary(boolean primary);
/**
* 返回···
*/
boolean isPrimary();
/**
* 设置FactoryBean名称
*/
void setFactoryBeanName(@Nullable String factoryBeanName);
/**
* 返回···
*/
@Nullable
String getFactoryBeanName();
/**
* 设置工厂方法名称,
* 如果有工厂方法,此方法将使用构造函数参数调用,如果没有指定参数则不使用。
*/
void setFactoryMethodName(@Nullable String factoryMethodName);
/**
* 返回···
*/
@Nullable
String getFactoryMethodName();
/**
* 设置该bean指定构造函数参数值。
*/
ConstructorArgumentValues getConstructorArgumentValues();
/**
* 返回···
*/
default boolean hasConstructorArgumentValues() {
return !getConstructorArgumentValues().isEmpty();
}
/**
* 获取要应用于bean实例的属性值。
*/
MutablePropertyValues getPropertyValues();
/**
* 返回···
*/
default boolean hasPropertyValues() {
return !getPropertyValues().isEmpty();
}
/**
* 设置初始化方法
*/
void setInitMethodName(@Nullable String initMethodName);
/**
* 返回···
*/
@Nullable
String getInitMethodName();
/**
* 设置销毁方法
*/
void setDestroyMethodName(@Nullable String destroyMethodName);
/**
* 返回···
*/
@Nullable
String getDestroyMethodName();
/**
* 设置角色
*/
void setRole(int role);
/**
* 获取···
*/
int getRole();
/**
* 设置bean描述
*/
void setDescription(@Nullable String description);
/**
* 返回bean描述
*/
@Nullable
String getDescription();
// Read-only attributes
/**
* 返回此bean定义的可解析类型。
*/
ResolvableType getResolvableType();
/**
* 是否单例
*/
boolean isSingleton();
/**
* 是否原型
*/
boolean isPrototype();
/**
* 是否抽象
*/
boolean isAbstract();
/**
* 返回此bean定义来自的资源的描述
*/
@Nullable
String getResourceDescription();
/**
* 返回原始bean定义,如果没有则为空。
*/
@Nullable
BeanDefinition getOriginatingBeanDefinition();
}
1. prepareRefresh()
prepareRefresh()
是refresh()
的第一个方法,prepareRefresh()
主要用于设置启动开始时间、活动标志位、初始化环境配置参数、校验参数等,来为初始化做好准备。
// AbstractApplicationContext#prepareRefresh()
protected void prepareRefresh() {
// 记录当前时间戳,切换活动状态
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
// 部分省略日志打印
// 初始化环境配置参数
// 当前类方法本身do nothing,
// 通过重写初始化环境属性信息。
initPropertySources();
// 获取initPropertySources中初始化的环境配置参数,
// 来进行验证标记为required的属性
getEnvironment().validateRequiredProperties();
// 载入预刷新应用程序监听
if (this.earlyApplicationListeners == null) {
this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
}
else {
// 将本地应用程序监听器重置为预刷新状态。
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
// 允许早期应用事件收集,
// 当multicaster可用就会发布
this.earlyApplicationEvents = new LinkedHashSet<>();
}
// AbstractApplicationContext#initPropertySources
protected void initPropertySources() {
// For subclasses: do nothing by default.
}
// AbstractPropertyResolver#validateRequiredProperties
@Override
public void validateRequiredProperties() {
MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
for (String key : this.requiredProperties) {
// 判断必需的环境配置参数是否为空
if (this.getProperty(key) == null) {
ex.addMissingRequiredProperty(key);
}
}
if (!ex.getMissingRequiredProperties().isEmpty()) {
throw ex;
}
}
prepareRefresh()大致流程如下:
- 记录当前时间戳并设置活动状态。
- 初始环境配置参数,校验必需的配置参数。(校验未通过则抛出
MissingRequiredPropertiesException: The following properties were declared as required but could not be resolved: [缺失的配置参数数组]
) - 初始化或清除并载入应用程序监听(ApplicationListener)。
- 初始化事件(ApplicationEvent)。
2. obtainFreshBeanFactory()
obtain(获得)fresh(最新的)BeanFactory
,该方法中会解析配置元数据,生成BeanDefinition,并注册到BeanFactory
中。
该方法链路比较长,最好结合源码一起看,接下来进入正题。
//AbstractApplicationContext#ConfigurableListableBeanFactory()
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
// 配置加载
refreshBeanFactory();
// 获取BeanFactory并返回
return getBeanFactory();
}
// AbstractRefreshableApplicationContext#refreshBeanFactory()
@Override
protected final void refreshBeanFactory() throws BeansException {
// 判断BeanFactory是否已经创建
if (hasBeanFactory()) {
// 销毁所有已创建的bean
destroyBeans();
// 将BeanFactory置空
closeBeanFactory();
}
try {
// 默认实现创建DefaultListableBeanFactory,
// 并将容器的父级内部bean工厂作为父bean工厂
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
// 1.设置是否允许覆盖同名bean定义,自动替换前一个定义。
// 2.设置是否允许bean之间循环引用。
customizeBeanFactory(beanFactory);
// 加载配置转换为BeanDefinition注册到beanFactory
loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
通过如上refreshBeanFactory()
源码,我们可以了解到大致流程:
-
判断BeanFactory是否已经创建,若已经创建则销毁所有已创建的bean、并把beanFactory置空。
// AbstractRefreshableApplicationContext#hasBeanFactory() protected final boolean hasBeanFactory() { return (this.beanFactory != null); }
-
创建
DefaultListableBeanFactory
(该类为BeanFactory
的一个默认实现),并将其设置为容器的parentBeanFactory
。
-
customizeBeanFactory()
设置是否允许覆盖bean定义及bean之间的循环引用。// AbstractRefreshableApplicationContext#customizeBeanFactory protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) { // 设置是否允许同名bean定义覆盖,true:自动替换前一个定义。 if (this.allowBeanDefinitionOverriding != null) { beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } // 设置是否允许bean之间循环引用。 if (this.allowCircularReferences != null) { beanFactory.setAllowCircularReferences(this.allowCircularReferences); } }
-
loadBeanDefinitions(beanFactory)
读取配置元数据转换为BeanDefinition
注册到beanFactory
。
IOC容器管理了一个或多个Bean。在容器本身,这些bean表示为BeanDefinition
对象,容器启动过程中,会将Bean解析为BeanDefinition
注册到beanFactory中。
loadBeanDefinitions(beanFactory)
开始套娃···
// AbstractXmlApplicationContext#loadBeanDefinitions
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 配置beanDefinitionReader与context在同一环境中
beanDefinitionReader.setEnvironment(this.getEnvironment());
// 使用当前上下文对象作为beanDefinitionReader的ResourceLoader
beanDefinitionReader.setResourceLoader(this);
// 设置用于解析的SAX实体解析器,比如解析dtd、xsd文件
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 初始化beanDefinitionReader
// 默认开启XML验证
initBeanDefinitionReader(beanDefinitionReader);
// 通过beanDefinitionReader加载BeanDefinition
// 加载配置转换为BeanDefinition注册到beanFactory
loadBeanDefinitions(beanDefinitionReader);
}
loadBeanDefinitions(beanFactory)
大致步骤:
- 实例化
XmlBeanDefinitionReader
(读取通过Xml文件中配置Bean定义的工具类)、属性配置。 - 加载配置转换为BeanDefinition注册到beanFactory。
loadBeanDefinitions(beanDefinitionReader)
// AbstractXmlApplicationContext#loadBeanDefinitions
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
// 可以通过子类重写getConfigResources的方式指定配置文件数组,
// getConfigResources()默认返回null
Resource[] configResources = getConfigResources();
if (configResources != null) {
// 从指定配置资源中加载Bean
reader.loadBeanDefinitions(configResources);
}
// 获取本地配置文件
String[] configLocations = getConfigLocations();
if (configLocations != null) {
// 从指定配置文件中加载Bean
reader.loadBeanDefinitions(configLocations);
}
}
loadBeanDefinitions(beanDefinitionReader)
大致步骤如下:
- 在子类重写
getConfigResources()
方法的前提下,通过重写方法指定逻辑获取配置文件数组(默认返回null),从指定配置文件中加载Bean。// AbstractXmlApplicationContext#getConfigResources @Nullable protected Resource[] getConfigResources() { return null; }
- 获取本地配置文件,从指定配置文件中解析并加载Bean。
reader.loadBeanDefinitions(configLocations)
// AbstractBeanDefinitionReader#loadBeanDefinitions
// 返回所有注册BeanDefinition的个数
@Override
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;
}
继续朝下追踪loadBeanDefinitions(location)
的内部处理逻辑。
loadBeanDefinitions(location)
// AbstractBeanDefinitionReader#loadBeanDefinitions
@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}
// AbstractBeanDefinitionReader#loadBeanDefinitions
/**
* 读取配置转换为BeanDefinition并注册到beanFactory
* @param location 本地资源文件路径
* @param actualResources 解析过程中的实际资源集合
*/
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
// 获取资源加载器
ResourceLoader resourceLoader = getResourceLoader();
// 部分省略
if (resourceLoader instanceof ResourcePatternResolver) {
try {
// 使用资源解析器根据资源文件路径解析并加载
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
// 解析当前资源中的bean定义,
// 并返回注册BeanDefinition个数
int count = loadBeanDefinitions(resources);
// 若actualResources不为null,则把文件资源添加到集合中
if (actualResources != null) {
Collections.addAll(actualResources, resources);
}
// 部分省略
// 返回注册个数
return count;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
Resource resource = resourceLoader.getResource(location);
int count = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
// 部分省略
return count;
}
}
loadBeanDefinitions(location, null)
大致流程如下:
-
getResourceLoader()
获得资源加载器。@Override @Nullable public ResourceLoader getResourceLoader() { // this.resourceLoader即AbstractXmlApplicationContext。 // 前面源码中有讲解, // 没有印象的可以再去回顾AbstractXmlApplicationContext#loadBeanDefinitions return this.resourceLoader; }
-
loader instanceof ResourcePatternResolver
= true
-
通过资源解析器根据资源文件路径解析并加载(该节点以下的源码,感兴趣可以看看,不感兴趣可以直接跳过)。
// PathMatchingResourcePatternResolver#getResources // 根据指定配置文件路径获取文件资源数组 @Override public Resource[] getResources(String locationPattern) throws IOException { // 部分省略 // 判断文件路径是否以‘classpath*’开头 // CLASSPATH_ALL_URL_PREFIX:classpath*: // “classpath*:”代表:该工程有多个classpath,同时寻找并加载多个classpath路径下的文件 // “classpath:” 代表: 与上反之 if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) { // 判断截取“classpath*:”后的文件路径是否包含“*”或“?”或“{”或“}” // 例如:配置为:“classpath*:config/*Config.xml”,则会寻找所有classpath路径config文件夹下所有以“Config.xml”为后缀的文件 if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) { // 根据路径查找匹配资源文件 return findPathMatchingResources(locationPattern); } else { // 若未包含以上字符,则寻找并加载所有classpath路径下的指定名称的配置文件。 return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length())); } } else { int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 : locationPattern.indexOf(':') + 1); // 判断是否包含特殊字符 if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) { return findPathMatchingResources(locationPattern); } else { // 单文件根据名称加载 // 我们的demo中直接走这个分支 return new Resource[] {getResourceLoader().getResource(locationPattern)}; } } }
具体查找文件的方法就不继续延伸了,有兴趣的可以展开阅读。
-
loadBeanDefinitions(resources)
解析资源所有的bean定义,并返回注册成功BeanDefinition
个数。
我们继续追踪loadBeanDefinitions(resources)
的内部处理逻辑。
loadBeanDefinitions(resources)
// AbstractBeanDefinitionReader#loadBeanDefinitions
@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
int count = 0;
for (Resource resource : resources) {
count += loadBeanDefinitions(resource);
}
return count;
}
// XmlBeanDefinitionReader#loadBeanDefinitions
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
/**
* 从指定文件中加载BeanDefinition并返回个数
*/
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
// 部分省略
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
// 获取资源文件字节流
try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// 从XML资源中加载BeanDefinitions
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
如上代码大致流程如下:
- 获取当前文件字节流,实例化
InputSource
。// InputSource public InputSource (InputStream byteStream){ setByteStream(byteStream); } public void setByteStream (InputStream byteStream){ this.byteStream = byteStream; }
- 设置资源编码格式,真正开始执行XML解析动作。
做了这么多准备动作,终于开始进入实际的加载,我们继续。
doLoadBeanDefinitions(inputSource, resource())
// XmlBeanDefinitionReader#doLoadBeanDefinitions
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 转换为Document对象
Document doc = doLoadDocument(inputSource, resource);
// 获取注册成功的BeanDefinition
int count = registerBeanDefinitions(doc, resource);
// 部分省略
return count;
}
// 部分省略
}
如何完成注册呢?
// XmlBeanDefinitionReader#registerBeanDefinitions
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 通过反射实例化并返回BeanDefinitionDocumentReader对象,
// 该对象用于解析xml格式的bean定义
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 获取当前已经注册的BeanDefinition个数
int countBefore = getRegistry().getBeanDefinitionCount();
// 注册BeanDefinition
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// 返回当前注册成功的BeanDefinition个数
return getRegistry().getBeanDefinitionCount() - countBefore;
}
以上流程大致如下:
- 通过反射实例化并返回用于解析xml格式的bean定义
BeanDefinitionDocumentReader
对象。 - 注册BeanDefinition。
- 返回当前注册成功的BeanDefinition个数。
registerBeanDefinitions(doc, createReaderContext(resource))
接下来我们来简单看看xml配置元数据的解析步骤。
// DefaultBeanDefinitionDocumentReader#registerBeanDefinitions
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}
/**
* 根据资源文件中给定的<beans/>,注册各个<bean/>
*/
// DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions
@SuppressWarnings("deprecation") // for Environment.acceptsProfiles(String...)
protected void doRegisterBeanDefinitions(Element root) {
// 任何的元素都会在此方法递归。
// 若当前parent为空则创建子delegate,并引用父delegate
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
// 确定给定的节点是否表明默认命名空间。
// 也就是我们配置文件的<beans/>的xmlns属性“http://www.springframework.org/schema/beans”
if (this.delegate.isDefaultNamespace(root)) {
// 获取当前<beans/>的profile属性名
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
// 判空
if (StringUtils.hasText(profileSpec)) {
// MULTI_VALUE_ATTRIBUTE_DELIMITERS=“,; ”
// 按照","或";"或" "作为分隔符分割
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// 获取spring.profiles.active属性值,判断specifiedProfiles是否包含,false则return
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
// 部分省略
return;
}
}
}
// 空方法,可以通过重写的方式前置处理
preProcessXml(root);
// 开始解析
parseBeanDefinitions(root, this.delegate);
// 空方法,通过重写后置处理
postProcessXml(root);
this.delegate = parent;
}
大致步骤如下:
-
创建
BeanDefinitionParserDelegate
,它是用于解析资源文件的委托类。 -
isDefaultNamespace(root)
检查被定义的命名空间是否是默认命名空间。// BeanDefinitionParserDelegate public boolean isDefaultNamespace(Node node) { return isDefaultNamespace(getNamespaceURI(node)); } // 这边获取到的就是<beans/>的xmlns属性,xmlns:命名空间 @Nullable public String getNamespaceURI(Node node) { return node.getNamespaceURI(); } // BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans" public boolean isDefaultNamespace(@Nullable String namespaceUri) { return !StringUtils.hasLength(namespaceUri) || BEANS_NAMESPACE_URI.equals(namespaceUri); }
-
第二步若为
true
则获取<beans/>
的profile
属性值,若该属性值不为空,进行检查当前节点是否满足环境配置。profile
用于指定当前运行环境的启用配置。示例:
public class XmlTest { static ApplicationContext context; static { context = new ClassPathXmlApplicationContext("classpath*:config/*Config.xml"); } @Test public void instanceBeanBaseOnConstructorByXml() { Car car = (Car) context.getBean("constructorCar"); System.out.println(car.toString()); } @Test public void secInstanceBeanBaseOnConstructorByXml() { Car car = (Car) context.getBean("secConstructorCar"); System.out.println(car.toString()); } }
修改demo中的xml文件配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <beans profile="dev test"> <bean id="constructorCar" class="com.zt.bean.Car"> <constructor-arg name="brand" value="Benz"/> <constructor-arg name="price" value="200"/> <constructor-arg name="color" value="white"/> </bean> </beans> <beans profile="dev"> <bean id="secConstructorCar" class="com.zt.bean.Car"> <constructor-arg name="brand" value="Benz"/> <constructor-arg name="price" value="200"/> <constructor-arg name="color" value="white"/> </bean> </beans> </beans>
修改环境参数:
执行结果:org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'secConstructorCar' available ··· Car(brand=Benz, price=200.0, color=white)
-
parseBeanDefinitions(root, this.delegate)
开始配置解析。
parseBeanDefinitions(root, this.delegate)
// DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// 判断是否是Spring默认命名空间,
// true,例如<beans/><import/><bean/><alias/>
// false,例如<context:component-scan>等
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 判断当前节点是否为一个有效元素,比如:注释
if (node instanceof Element) {
Element ele = (Element) node;
// 判断方式如上
if (delegate.isDefaultNamespace(ele)) {
// 默认命名空间元素解析
parseDefaultElement(ele, delegate);
}
else {
// 非默认命名空间元素解析
delegate.parseCustomElement(ele);
}
}
}
}
else {
// 非默认命名空间元素解析
delegate.parseCustomElement(root);
}
}
我们先查看默认命名空间元素是如何解析的:
// DefaultBeanDefinitionDocumentReader#parseDefaultElement
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
// 判断当前节点名称是否为‘import’
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
// 判断当前节点名称是否为‘alias’
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
// 判断当前节点名称是否为‘bean’
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
// 判断当前节点名称是否为‘beans’
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// 递归<beans/>下的所有节点
doRegisterBeanDefinitions(ele);
}
}
本文暂时只看processBeanDefinition(ele, delegate)
分支。
processBeanDefinition(ele, delegate)
// DefaultBeanDefinitionDocumentReader#processBeanDefinition
// 处理给定的bean元素,解析bean的定义并注册。
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 解析当前<bean/>转换为BeanDefinitionHolder
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// <bean/>自定义属性及子标签解析
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// BeanDefinition注册
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// 触发注册事件
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
大致处理流程如下:
delegate.parseBeanDefinitionElement(ele)
将<bean/>
定义解析为BeanDefinitionHolder
。BeanDefinitionHolder
持有着BeanDefinition、beanName及aliases(别名)。public class BeanDefinitionHolder implements BeanMetadataElement { private final BeanDefinition beanDefinition; private final String beanName; @Nullable private final String[] aliases; // 省略 }
<bean/>
自定义属性及子标签解析。- BeanDefinition注册。
我们先从第一步,查看如何解析为BeanDefinitionHolder
。
parseBeanDefinitionElement(ele)
// BeanDefinitionParserDelegate#parseBeanDefinitionElement
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 获取<bean/>的id
String id = ele.getAttribute(ID_ATTRIBUTE);
// 获取<bean/>的name
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<>();
// 获取是否有name属性的定义
if (StringUtils.hasLength(nameAttr)) {
// 将名称按照","或";"或" "作为分隔符分割
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
// 若<bean/>未定义ID属性,并且name属性不为空,
// 则beanName=aliases.remove(0)
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
// 部分省略
}
if (containingBean == null) {
// 名称唯一性检查
checkNameUniqueness(beanName, aliases, ele);
}
// 创建BeanDefinition实例
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
// 若<bean/>标签未指定id与name,则为true
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
// 生成beanName
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
// 生成beanName为类全限定名+容器中已存在的名称个数+1(从0开始)
// 例如 <bean class="com.zt.bean.Car" />
// beanName = com.zt.bean.Car#0
beanName = this.readerContext.generateBeanName(beanDefinition);
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
// 部分省略
} catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
代码步骤大致如下:
- 获取
<bean>
的id、name以及aliases等信息,判断IOC容器中是否存在同名beanName。 parseBeanDefinitionElement(ele, beanName, containingBean)
创建BeanDefinition实例。- 处理未设置id与name的
<bean>
定义。 - 返回
BeanDefinitionHolder
,通过构造方法设置beanDefinition、beanName、aliases。
第二步是怎么创建BeanDefinition实例的呢?
// BeanDefinitionParserDelegate#parseBeanDefinitionElement
/**
* 解析bean定义,过程中出现异常则返回null
*/
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
// 判断是否存在class属性
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
// 判断是否存在parent属性。
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
// 创建AbstractBeanDefinition
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 将<bean/>上定义的属性set到bd对象
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// 解析<meta/>
parseMetaElements(ele, bd);
// 解析<lookup-method/>
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 解析<replaced-method/>
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 解析<constructor-arg/>
parseConstructorArgElements(ele, bd);
// 解析<property/>
parsePropertyElements(ele, bd);
// 解析<qualifier/>
parseQualifierElements(ele, bd);
// 设置对应配置文件Resource
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
// 部分省略
finally {
this.parseState.pop();
}
return null;
}
我们先回顾一下<bean>
标签的属性及子标签:
<bean>标签属性 | |
---|---|
id | 设置在容器中的唯一标识 |
name | bean的名称定义 |
class | 类全限定名 |
scope | singleton:默认单例;prototype:多例 |
init-method | 类初始化方法 |
destory-method | 类销毁方法 |
parent | 指代“父bean”,不指代父类。 |
primary | 是否注入优先,默认:false |
abstract | 声明为抽象bean |
depends-on | 声明该bean与其它bean的依赖关系 |
factory-bean | 指定bean工厂 |
factory-method | 指定bean工厂方法 |
autowire | 自动装配模式(1)no:默认值;(2)byName:根据bean名称自动装配;(3)byType:按照类型进行装配;(4)constructor:通过构造器自动装配;(5)default:使用<beans/>的default-autowire属性配置。 |
autowire-candidate | 默认为true,表示允许自动装配注入到其它bean |
lazy-init | 默认为false,表示在IOC容器启动的时候进行实例化;true,则代表使用的时候进行实例化。 |
<bean>标签的子标签 | |
---|---|
<meta> | 源数据设置 |
<property> | bean成员变量属性设置 |
<constructor-arg> | 构造方法参数设置 |
<description> | 描述信息 |
<lookup-method> | 方法注入 |
<qualifier> | 指定注入bean名称 |
<replaced-method> | 方法替换 |
大致流程如下:
- 创建
AbstractBeanDefinition
。 - 解析
<bean>
属性。/** * 解析<bean>标签属性配置赋予到bd */ // BeanDefinitionParserDelegate#parseBeanDefinitionAttributes public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName, @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) { // 判断是否存在single属性配置 if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) { error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele); } // 是否存在scope属性 else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) { bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE)); } else if (containingBean != null) { // Take default from containing bean in case of an inner bean definition. bd.setScope(containingBean.getScope()); } // 是否存在abstract属性 if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) { bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE))); } // 设置lazt-init属性,默认false String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE); if (isDefaultValue(lazyInit)) { lazyInit = this.defaults.getLazyInit(); } bd.setLazyInit(TRUE_VALUE.equals(lazyInit)); // 设置自动装配模式默认为0及默认自动装配 String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE); bd.setAutowireMode(getAutowireMode(autowire)); // 是否存在depends-on属性 if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) { String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE); bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS)); } // 是否存在autowire-candidate属性,默认为true String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE); if (isDefaultValue(autowireCandidate)) { String candidatePattern = this.defaults.getAutowireCandidates(); if (candidatePattern != null) { String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern); bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName)); } } else { bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate)); } // 是否存在primary属性 if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) { bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE))); } // 是否存在init-method属性 if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) { String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE); bd.setInitMethodName(initMethodName); } else if (this.defaults.getInitMethod() != null) { bd.setInitMethodName(this.defaults.getInitMethod()); bd.setEnforceInitMethod(false); } // 是否存在destroy-method属性 if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) { String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE); bd.setDestroyMethodName(destroyMethodName); } else if (this.defaults.getDestroyMethod() != null) { bd.setDestroyMethodName(this.defaults.getDestroyMethod()); bd.setEnforceDestroyMethod(false); } // 是否存在factory-method属性 if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) { bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE)); } // 是否存在factory-bean属性 if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) { bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE)); } return bd; }
- 解析
<bean>
子标签(源码就不在这里贴了,可以自行查看)。 - 返回解析并组装完毕后的
AbstractBeanDefinition
。
BeanDefinition
已经获取到了,接下来就进行到注册这一步了,接下来我们来查看它的处理逻辑。
registerBeanDefinition(bdHolder, getReaderContext().getRegistry())
/**
* 注册到容器
*/
// BeanDefinitionReaderUtils#registerBeanDefinition
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// 使用beanName注册BeanDefinition
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 别名注册
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
跟进查看注册逻辑:
// DefaultListableBeanFactory#registerBeanDefinition
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
// beanDefinition校验
((AbstractBeanDefinition) beanDefinition).validate();
}
// 部分省略
}
// 按beanName从beanDefinitionMap获取beanDefinition
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
// 若根据beanName可以获取到beanDefinition,
// 则判断该beanDefinition是否允许覆盖
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
// 部分省略
// 允许覆盖的前提下,将当前beanDefinition放到beanDefinitionMap中
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
// 检查是否存在被标记为已创建状态的bean
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
removeManualSingletonName(beanName);
}
}
else {
// 进入启动注册阶段
// key:beanName,value:BeanDefinition,将当前beanDefinition放到beanDefinitionMap中
this.beanDefinitionMap.put(beanName, beanDefinition);
// 将BeanDefinition的名称add到beanDefinitionNames中(for)
this.beanDefinitionNames.add(beanName);
// 处理environment、systemProperties、systemEnvironment、applicationStartup等bean
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (existingDefinition != null || containsSingleton(beanName)) {
// 重置当前beanName的所有bean定义
resetBeanDefinition(beanName);
}
else if (isConfigurationFrozen()) {
clearByTypeCache();
}
}
至此我们也终于大致阅读完obtainFreshBeanFactory()
的源码,其它具体细节有兴趣可以自行阅读。
总结
至此,我们已经了解了refresh()
核心方法的前两个prepareRefresh()
和obtainFreshBeanFactory()
方法,从初始化并校验环境配置,初始化应用监听与事件,到解析资源配置文件,将文件中关于bean定义加载到beanFactory并返回beanFactory的过程。