Spring 源码系列-BeanDefinition

Bean的定义主要由 BeanDefinition 来描述的。作为Spring中用于包装Bean的数据结构,今天就来看看它的面纱下的真容吧

BeanDefinition 类定义

首先就是BeanDefinition的类定义:

1
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement

对,没错,这货是个接口,而不是类,是不是有点莫名奇妙呢?我们都知道在JAVA中,接口是不能用来new出新的对象的,那么在Spring中,到底将XML解析出来的Bean包装成了什么呢?(这个密等下揭开)

先来看看BeanDefinition一个继承结构吧(均是与BeanDefinition有直接关联的类或者接口)!


从类图中可以看出,BeanDefinition继承了AttributeAccessor和BeanMetadataElement两个接口;一个一个看。

AttributeAccessor

AttributeAccessor 接口定义了最基本的对任意对象的元数据的修改或者获取,主要方法有:

1
2
3
4
5
6
7
8
9
10
11
//将name定义的属性设置为提供的value值。如果value的值为null,则该属性为{@link #removeAttribute removed}。
//通常,用户应该注意通过使用完全限定的名称(可能使用类或包名称作为前缀)来防止与其他元数据属性重叠。
void setAttribute(String name, Object value);
//获取标识为name的属性的值。
Object getAttribute(String name);
//删除标识为name的属性,并返回属性值
Object removeAttribute(String name);
//如果名为name的属性是否存在,存在返回true,否则返回false。
boolean hasAttribute(String name);
//返回所有属性的名称。
String[] attributeNames();

BeanMetadataElement

BeanMetadataElement 接口提供了一个 getResource() 方法,用来传输一个可配置的源对象。

1
2
//返回此元数据元素的配置源对象(可能为null)。
Object getSource();

BeanDifinition 源码分析

一个BeanDefinition描述了一个bean的实例,包括属性值,构造方法参数值和继承自它的类的更多信息。BeanDefinition仅仅是一个最简单的接口,主要功能是允许BeanFactoryPostProcessor 例如PropertyPlaceHolderConfigure 能够检索并修改属性值和别的bean的元数据(译注)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//标准单例作用域的作用域标识符:“singleton”。
//对于扩展的bean工厂可能支持更多的作用域。
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
//标准原型作用域的范围标识符:“prototype”。
//对于扩展的bean工厂可能支持更多的作用域。
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
//表示BeanDefinition是应用程序主要部分的角色提示。 通常对应于用户定义的bean。
int ROLE_APPLICATION = 0;
//表示BeanDefinition是某些大型配置的支持部分的角色提示,通常是一个外部ComponentDefinition。
//当查看某个特定的ComponentDefinition时,认为bean非常重要,
//以便在查看应用程序的整体配置时能够意识到这一点。
int ROLE_SUPPORT = 1; //1实际上就是说,我这个Bean是用户的,是从配置文件中过来的。
//角色提示表明一个BeanDefinition是提供一个完全背景的角色,并且与最终用户没有关系。
//这个提示用于注册完全是ComponentDefinition内部工作的一部分的bean
int ROLE_INFRASTRUCTURE = 2; //2就是我这Bean是Spring自己的,和你用户没有一毛钱关系。

上面是BeanDifinition的一些基本属性信息,一个就是标识下当前Bean的作用域,另外就是标识一下这个Bean是内部的还是外部的。下面来看这个接口为其子类都提供了哪些具体的行为方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
//如果父类存在,设置这个bean定义的父定义的名称。
void setParentName(String parentName);
//如果父类存在,则返回当前Bean的父类的名称
String getParentName();
//指定此bean定义的bean类名称。
//类名称可以在bean factory后期处理中修改,通常用它的解析变体替换原来的类名称。
void setBeanClassName(String beanClassName);
//返回此bean定义的当前bean类名称。
//需要注意的是,这不一定是在运行时使用的实际类名,以防子类定义覆盖/继承其父类的类名。
//此外,这可能只是调用工厂方法的类,或者它 在调用方法的工厂bean引用的情况下甚至可能是空的。
//因此,不要认为这是在运行时定义的bean类型,而只是将其用于在单独的bean定义级别进行解析。
String getBeanClassName();
//覆盖此bean的目标范围,指定一个新的范围名称。
void setScope(String scope);
//返回此bean的当前目标作用域的名称,如果没有确定,返回null
String getScope();
//设置这个bean是否应该被延迟初始化。如果{false},那么这个bean将在启动时由bean工厂实例化,
//这些工厂执行单例的立即初始化。
//懒加载 <bean lazy-init="true/false">
void setLazyInit(boolean lazyInit);
//返回这个bean是否应该被延迟初始化,即不是在启动时立即实例化。只适用于单例bean。
boolean isLazyInit();
//设置这个bean依赖被初始化的bean的名字。 bean工厂将保证这些bean首先被初始化。
//<bean depends-on="">
void setDependsOn(String... dependsOn);
//返回这个bean依赖的bean名称。
String[] getDependsOn();
//设置这个bean是否是获得自动装配到其他bean的候选人。
//需要注意是,此标志旨在仅影响基于类型的自动装配。
//它不会影响按名称的显式引用,即使指定的bean没有标记为autowire候选,也可以解决这个问题。
//因此,如果名称匹配,通过名称的自动装配将注入一个bean。
void setAutowireCandidate(boolean autowireCandidate);
//返回这个bean是否是自动装配到其他bean的候选者。就是是否在其他类中使用autowired来注入当前Bean的
//是否为被自动装配 <bean autowire-candidate="true/false">
boolean isAutowireCandidate();
//是否为主候选bean 使用注解:@Primary
void setPrimary(boolean primary);
//返回这个bean是否是主要的autowire候选者。
boolean isPrimary();
//指定要使用的工厂bean(如果有的话)。 这是调用指定的工厂方法的bean的名称。
void setFactoryBeanName(String factoryBeanName);
//返回工厂bean的名字,如果有的话。
String getFactoryBeanName();
//如果有的话,指定工厂方法。
//这个方法先将通过构造函数参数被调用,或者如果参数,将调用该方法的无参数构造。
//方法将在指定的工厂bean(如果有的话)上被调用,或者作为本地bean类的静态方法被调用。
void setFactoryMethodName(String factoryMethodName);
//如果存在,返回工厂方法名
String getFactoryMethodName();
//返回此bean的构造函数参数值。
ConstructorArgumentValues getConstructorArgumentValues();
//获取普通属性集合
MutablePropertyValues getPropertyValues();
//是否是单例的
boolean isSingleton();
//是否是多例的
boolean isPrototype();
//是否是抽象类
boolean isAbstract();
//获取这个bean的应用
int getRole();
//返回对bean定义的可读描述。
String getDescription();
//返回该bean定义来自的资源的描述
String getResourceDescription();
//返回原始的BeanDefinition;如果没有,则返回null。允许检索装饰的bean定义(如果有的话)。
//注意,这个方法返回直接的发起者。 迭代原始链,找到用户定义的原始BeanDefinition。
BeanDefinition getOriginatingBeanDefinition();

从上面的属性和方法分析可以看出,BeanDefinition 对于一个Bean的描述做了较为完整的一套约束。这为后续的子类提供的最基本的职责和属性。

beanFactory 创建

Spring的Ioc容器其实就是一个bean的关系网,依赖于core,bean,context三个组件来构建的。在spring中最核心的就是对于bean的管理。而bean又依托于我们的容器。本文将从顶层分析一下spring中beanFactory的具体创建过程,为后续的bean的生命周期提供一个基础。

BeanFactory的继承体系


从上图可以看到,BeanFactory有三个子类:

  • ListableBeanFactory
  • HierarchicalBeanFactory
  • AutowireCapableBeanFactory

(上述三个类的子类体系小伙伴们可以自己对着源码看下,实在太多)

看下上图中最底层的DefaultListableBeanFactory类的定义:

1
2
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable

这个其实就是BeanFactory的默认实现类,它直接或者间接的实现了所有的接口。其实在看spring源码的时候都会遇到类似的设计模式,对于某一个具体的功能,通常都会定义很多层的接口,层层包装,层层委托。这种做法的好处就是,对于不同的场合都会有特定的接口;这样一来就可以在spring内部对对象的传递和转化操作都会有一些访问限制。

例如ListableBeanFactory接口表示这些Bean是可列表的,而HierarchicalBeanFactory表示的是这些Bean是有继承关系的,也就是每个Bean有可能有父Bean。AutowireCapableBeanFactory接口定义Bean的自动装配规则。这四个接口共同定义了Bean的集合、Bean之间的关系、以及Bean行为。

BeanFactory的创建

在之前的文章中说过了容器的刷新过程。BeanFactory的创建也在wac.refresh()方法中。具体看下到底是通过哪些子类来完成的:

1
2
// 通知子类刷新内部的bean工厂。
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

1.AbstractApplicationContext中的obtainFreshBeanFactory

下面是obtainFreshBeanFactory的方法逻辑:

1
2
3
4
5
6
7
8
9
10
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//这个是具体创建的方法,由子类实现
refreshBeanFactory();
//获取BeanFactory实例对象(ConfigurableListableBeanFactory类型的)
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}

refreshBeanFactory并未有具体的实现逻辑,这个方法主要是通过委托给子类的refreshBeanFactory方法来实现,在AbstractApplicationContext中refreshBeanFactory是一个抽象模板方法:

1
protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;

2.refreshBeanFactory方法(AbstractRefreshableApplicationContext类中):

下面只注释与beanFactory创建相关的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
protected final void refreshBeanFactory() throws BeansException {
//是否已经有BeanFactory了
if (hasBeanFactory()) {
//销毁原有的Bean
destroyBeans();
//关闭工厂
closeBeanFactory();
}
try {
//创建一个新的beanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition
source for " + getDisplayName(), ex);
}
}

这个方法是实现执行这个上下文的底层bean工厂的实际刷新,如果有的话之前有BeanFactory存在,则关闭以前的bean工厂。并为上下文生命周期的下一个阶段初始化一个新鲜的bean工厂。

3.createBeanFactory(AbstractRefreshableApplicationContext类中)

1
2
3
protected DefaultListableBeanFactory createBeanFactory() {
return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}

这个方法就是为当前上下文创建一个内部的bean工厂。每次调用refresh()方法是都会创建尝试创建。默认实现是创建一个DefaultListableBeanFactory。并通过getInternalParentBeanFactory()获取内部bean工厂来作为父级bean工厂。可以在子类中重写,例如自定义DefaultListableBeanFactory的设置。

getInternalParentBeanFactory(AbstractApplicationContext类中)

1
2
3
4
protected BeanFactory getInternalParentBeanFactory() {
return (getParent() instanceof ConfigurableApplicationContext) ?
((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent();
}

4.DefaultListableBeanFactory的构造函数

1
2
3
4
5
6
7
/**
* 通过给定的父类创建一个新的DefaultListableBeanFactory容器
* @param parentBeanFactory the parent BeanFactory
*/
public DefaultListableBeanFactory(BeanFactory parentBeanFactory) {
super(parentBeanFactory);
}

super(parentBeanFactory)调用的是AbstractAutowireCapableBeanFactory的构造函数

1
2
3
4
5
6
7
8
9
/**
* 通过给定的父类构建新的AbstractAutowireCapableBeanFactory
* @param parentBeanFactory parent bean factory, or {@code null} if none
*/
public AbstractAutowireCapableBeanFactory(BeanFactory parentBeanFactory) {
this();
//设置父工厂
setParentBeanFactory(parentBeanFactory);
}

this(),还是AbstractAutowireCapableBeanFactory的构造函数:

1
2
3
4
5
6
7
8
9
/**
* 构建一个新的AbstractAutowireCapableBeanFactory.
*/
public AbstractAutowireCapableBeanFactory() {
super();
ignoreDependencyInterface(BeanNameAware.class);
ignoreDependencyInterface(BeanFactoryAware.class);
ignoreDependencyInterface(BeanClassLoaderAware.class);
}

super() ; AbstractBeanFactory的构造函数

1
2
3
4
5
/**
* 构建一个新的 AbstractBeanFactory.
*/
public AbstractBeanFactory() {
}

BeanDefinition 载入(上)

继上一篇BeanFactory的创建之后,其实就是BeanDefinition载入了。同样也是在AbstractRefreshableApplicationContext类的refreshBeanFactory方法中完成:

//创建默认的DefaultListableBeanFactory工厂
DefaultListableBeanFactory beanFactory = createBeanFactory();
//设置Id
beanFactory.setSerializationId(getId());
//这个方法其实就是设置了allowBeanDefinitionOverriding和allowCircularReferences两个属性
customizeBeanFactory(beanFactory);

//调用子类的加载bean定义方法,这里会调用XmlWebApplicationContext子类的复写方法
loadBeanDefinitions(beanFactory);

这里的loadBeanDefinitions也是一个抽象方法,AbstractRefreshableApplicationContext类中并没有给出具体的实现,二是通过子类XmlWebApplicationContext的loadBeanDefinitions完成具体实现。

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) 
throws BeansException, IOException
{
//创建XmlBeanDefinitionReader,并通过回调设置到BeanFactory中
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
//为XmlBeanDefinitionReader配置Environment
beanDefinitionReader.setEnvironment(getEnvironment());
//为XmlBeanDefinitionReader配置ResourceLoader,
//因为DefaultResourceLoader是父类,所以this可以直接被使用
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

// 允许子类提供reader的自定义初始化,然后继续实际加载bean定义。
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}

initBeanDefinitionReader初始化用于加载此上下文的bean定义的bean定义读取器;默认实现是空的。然后下面通过重载的loadBeanDefinitions来做具体的bean解析(这里用到的是XmlBeanDefinitionReader这个解析器);使用给定的XmlBeanDefinitionReader加载bean definitions。

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
String[] configLocations = getConfigLocations();
//遍历xml文件
if (configLocations != null) {
for (String configLocation : configLocations) {
reader.loadBeanDefinitions(configLocation);
}
}
}

此时会将我们的applicationContext.xml读入(当然如何还有其他的spring配置文件,同样会一块拿到路径),如下图所示:

然后继续通过loadBeanDefinitions的重载方法继续委托调用。最后交给AbstractBeanDefinitionReader的loadBeanDefinitions来完成;这个代码比较长,拆开一步一步来说,先看下整体的:

public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
//获取ResourceLoader资源定位器
ResourceLoader resourceLoader = getResourceLoader();
//如果定位器为null,则抛出异常
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
//是否是ResourcePatternResolver类型的定位器
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int loadCount = loadBeanDefinitions(resources);
if (actualResources != null) {
for (Resource resource : resources) {
actualResources.add(resource);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
}
return loadCount;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
//非ResourcePatternResolver类型的
else {
// Can only load single resources by absolute URL.
Resource resource = resourceLoader.getResource(location);
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
}
return loadCount;
}
}

上面的代码中需要说明下为什么要判断当前resourceLoader是否是ResourcePatternResolver类型的,因为ResourceLoader只是提供了对classpath前缀的支持。而ResourcePatternResolver提供了对classpath*前缀的支持。也就是说ResourceLoader提供classpath下单资源文件的载入,而ResourcePatternResolver提供多资源文件的载入。
先看下假如是ResourcePatternResolver类型的(略去了部分log代码):

try {
//先得到我们的resources
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
//解析并返回beanDefinition的数量
int loadCount = loadBeanDefinitions(resources);
//加载过程中已经被解析过的实际的Resource的填充集合
if (actualResources != null) {
for (Resource resource : resources) {
actualResources.add(resource);
}
}
return loadCount;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}

非ResourcePatternResolver类型情况:

// Can only load single resources by absolute URL.
//只能通过绝对URL加载单个资源
Resource resource = resourceLoader.getResource(location);
//解析并返回beanDefinition的数量
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
return loadCount;

然后继续通过重载方法loadBeanDefinitions(Resource… resources)来解析(AbstractBeanDefinitionReader类中)

public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
//初始化beanDefiniton个数
int counter = 0;
//遍历当前资源数组
for (Resource resource : resources) {
//解析具体resource中的bean
counter += loadBeanDefinitions(resource);
}
return counter;
}

然后交给子类XmlBeanDefinitionReader中的loadBeanDefinitions(Resource resource)方法:

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}

继续通过重载方法loadBeanDefinitions(EncodedResource encodedResource)执行,这个方法我们只关注最核心的代码:

//获取输入流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
//资源读取inputSource
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//委托给doLoadBeanDefinitions来完成
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}

doLoadBeanDefinitions是XmlBeanDefinitionReader中的方法,来看核心代码:

//解析成符合w3c标准的Document
Document doc = doLoadDocument(inputSource, resource);
//继续交给registerBeanDefinitions来处理
return registerBeanDefinitions(doc, resource);

这个时候已经将loadBeanDefinitions换成registerBeanDefinitions了,也就是载入并注册;registerBeanDefinitions同样也是XmlBeanDefinitionReader中的方法:

public int registerBeanDefinitions(Document doc, Resource resource) throws
BeanDefinitionStoreException
{
//得到documentReader用来读取document文档
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//注册之前的bean个数
int countBefore = getRegistry().getBeanDefinitionCount();
//解析并注册bean
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}

仍然没有处理,继续交给BeanDefinitionDocumentReader的registerBeanDefinitions方法来完成:

//这个实现根据“spring-beans”XSD(或DTD)解析bean定义。
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);
}

还是没处理,又细化一步,交给DefaultBeanDefinitionDocumentReader的doRegisterBeanDefinitions(Element root)方法:

protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isInfoEnabled()) {
logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}

任何嵌套的<beans>元素都将导致此方法的递归。 为了正确传播和保存<beans> default- *属性,请跟踪当前(父)委托,该委托可能为null。 为了回退的目的,创建一个引用父对象的新(子)委托,然后最终重置this.delegate回到它的原始(父)引用。这个行为模仿了一堆委托,而实际上并不需要一个委托。(翻译的有点蹩脚,大概意思就是这)

所以说DefaultBeanDefinitionDocumentReader自己也没干这事,又给了BeanDefinitionParserDelegate,然后就是preProcessXml()、parseBeanDefinitions()、postProcessXml()方法;其中preProcessXml()和postProcessXml()默认是空方法,自己没有实现。具体解析在parseBeanDefinitions(root, this.delegate)中完成。

BeanDefinitionParserDelegate用于将 Document 的内容转成 BeanDefinition实例;BeanDefinitionDocumentReader 本身不具备该功能而是交给了该类来完成。

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//查看定义的命名空间是否为默认的命名空间
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);
}
}

这个方法就是解析文档中根目录下的元素:“import”,“alias”,“bean”。

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//解析一个“import”元素,并将给定资源的bean定义加载到bean工厂中。
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//处理给定的别名元素,向注册表注册别名。
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//处理给定的bean元素,解析bean定义并将其注册到注册表中。
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
//在给定的根<beans />元素内注册每个bean定义。
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}

先来看processBeanDefinition这个方法;

BeanDefinitionHolder是一个BeanDefinition的持有者,其定义了一下变量,并对以下变量提供get和set操作。这个在后面的说道BeanDefinition体系的时候再聊。

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//获取一个BeanDefinitionHolder
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
//首先根据自定义属性进行装饰。
//基于自定义嵌套元素进行装饰。
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 注册最终装饰的实例。
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));
}
}

接着看registerBeanDefinition这个方法:通过给定的bean工厂注册给定的bean definition 。

public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)

throws BeanDefinitionStoreException
{

// 在主名称下注册bean定义。
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

// 如果有的话,注册bean名称的别名,
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}

registerBeanDefinition里面又通过调用BeanDefinitionRegistry接口的实现DefaultListableBeanFactory来完成具体的注册过程。

BeanDefinition载入(中)

上面对于一些细节问题没有进行细究,比如说元素属性值的处理,构造函数的处理等等。本篇就学习记录一下相关点。

首先来看下是在哪个地方具体生成BeanDefinitiond的。下面是方法请求的顺序。

    1. DefaultBeanDefinitionDocumentReader.parseDefaultElement
    1. DefaultBeanDefinitionDocumentReader.processBeanDefinition
    1. BeanDefinitionParserDelegate.parseBeanDefinitionElement

关于元素的解析绝大多数都是在 BeanDefinitionParserDelegate 及其子类中完成的。OK,来看下 parseBeanDefinitionElement 这个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {

this.parseState.push(new BeanEntry(beanName));
String className = null;
//在这里是读取<bean>的class名字,然后载入到BeanDefinition中,并未做实例化
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}

try {
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
//生成BeanDefinition对象
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//解析当前bean的属性
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
//设置description信息
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
//对bean的元素信息进行解析
parseMetaElements(ele, bd);
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//解析bean的构造函数设置
parseConstructorArgElements(ele, bd);
//解析property设置
parsePropertyElements(ele, bd);
parseQualifierElements(ele, bd);

bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));

return bd;
}
//异常1:ClassNotFoundException
catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
}
//异常2:NoClassDefFoundError
catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
}
//其他未知错误
catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
}
finally {
this.parseState.pop();
}

return null;
}

此处我们以解析property为例,看下具体的处理细节:

1
2
3
4
5
6
7
8
9
10
11
12
13
//解析给定bean元素的属性子元素。
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
//获取子元素节点
NodeList nl = beanEle.getChildNodes();
//遍历
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//是否包含property标识
if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
parsePropertyElement((Element) node, bd);
}
}
}

接着是执行具体property,在parsePropertyElement中完成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//解析一个property元素。
public void parsePropertyElement(Element ele, BeanDefinition bd) {
//首先获取到property的名称
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
//检查是否有name
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
this.parseState.push(new PropertyEntry(propertyName));
try {
//验证在同一个bean中存在同名的property,存在的话就不解析了,直接返回
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
//解析出property的值
Object val = parsePropertyValue(ele, bd, propertyName);
//封装成PropertyValue对象
PropertyValue pv = new PropertyValue(propertyName, val);
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
this.parseState.pop();
}
}

在parsePropertyValue中,是对所有的property子元素进行具体解析的。我们知道property中除了单值之外,还会包括如:list,set,map,prop等集合元素;这些都会被封装成对应的Managerd对象。比如:ManagedList等。不同的集合类型页同样对应一种解析方法,比如解析list的是使用parseListElement。这些解析都是在BeanDefinitionParserDelegate类中完成的。这个后面我会抽一篇来学习BeanDefinitionParserDelegate这个类。

Bean的载入过程就是这样通过层层解析来完成的,但是对于目前的Ioc容器来说,仅仅是完成了对Bean对象管理的一些数据准备工作,也就是初始化工作,目前的BeanDefginiton中包含的就是一些静态的配置信息,Bean的实例化还没有进行,这个实例化的过程是在依赖注入时候完成的。

BeanDefinition载入(下)

BeanDefinition载入(上)中已经大概捋了一下解析过程,本篇将记录一下bean的注册过程。

bean的注册就是DefaultListableBeanFactory中registerBeanDefinition方法来完成的。那我就来看registerBeanDefinition这个方法的具体逻辑。

1、beanDefinition类型判断和验证

这里的验证主要是验证不能将静态工厂方法与方法重写相结合(静态工厂方法必须创建实例);

if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(),
beanName,"Validation of bean definition failed", ex);
}
}

2、尝试从beanDefinitionMap中获取老的bean

这里就是先根据beanName从beanDefinitionMap去取BeanDefinition,并将结果给oldBeanDefinition。

BeanDefinition oldBeanDefinition;
oldBeanDefinition = this.beanDefinitionMap.get(beanName);

3、beanDefinitionMap中已经存在名为beanName的Beandefinition

如果当前beanDefinitionMap中已经存在名为beanName的Beandefinition了(即检查是否有相同名称的beanDefinition已经在Ioc容器中注册过了)。,如果有,则进行以下具体策略:

  • 如果不允许bean被覆盖,则直接抛出不能重新注册,bean已经存在这样的异常信息
  • 使用框架生成的Bean来代替用户自定义的bean
  • 覆盖原有的Beandefinition
if (oldBeanDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
//省略异常代码
}
else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
//省略异常代码
}
else if (!beanDefinition.equals(oldBeanDefinition)) {
//提示覆盖log信息
}
else {
//提示覆盖log信息
}
//覆盖原有的Beandefinition
this.beanDefinitionMap.put(beanName, beanDefinition);
}

4、beanDefinitionMap不存在名为beanName的Beandefinition

//检查bean的创建阶段是否已经开始,也就是说是否已经创建了
if (hasBeanCreationStarted()) {
//Cannot modify startup-time collection elements anymore (for stable iteration)
// 无法修改启动时间收集元素(用于稳定迭代)(译注)
//注册过程需要保证数据的一致性,所有需要加锁同步
synchronized (this.beanDefinitionMap) {
//注册到beanDefinitionMap中
this.beanDefinitionMap.put(beanName, beanDefinition);
//下面就是将当前beanName存放到beanDefinitionNames中
List<String> updatedDefinitions = new ArrayList<String>(
this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
//如果单例模式的bean名单中有该bean的name,那么移除掉它。
//也就是说着,将一个原本是单例模式的bean重新注册成一个普通的bean
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new
LinkedHashSet<String>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
// 仍处于启动阶段,bean还没有开始注册
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;

5、执行缓存清除

  • 1:oldBeanDefinition如果存在,且执行到了这里也没有抛出异常,说明该BeanDefinition已经被覆盖,缓存需要更新。

  • 2:如果是单例模式的bean对象则Set中包含该beanName,执行到这里说明该BeanDefinition已经从一个单例模式的bean变为了一个普通的bean,所以缓存也需要更新。

if (oldBeanDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}

OK,我们来看下resetBeanDefinition这个方法:

这个方法的作用就是重置给定bean的所有bean定义缓存,包括从它派生的bean的缓存。

protected void resetBeanDefinition(String beanName) {
// 如果已经创建,则删除给定bean的合并bean定义。
clearMergedBeanDefinition(beanName);

// 如果有的话,从singleton 高速缓存中删除相应的bean。
//但是这也不是必须的,而只是为了覆盖上下文的默认bean
//(就是从manualSingletonNames中移除)
destroySingleton(beanName);
//递归的方式来 重置具有给定bean作为父项的所有bean定义。
for (String bdName : this.beanDefinitionNames) {
if (!beanName.equals(bdName)) {
BeanDefinition bd = this.beanDefinitionMap.get(bdName);
if (beanName.equals(bd.getParentName())) {
resetBeanDefinition(bdName);
}
}
}
}

Bean的注册就到这里了

作者

卫恒

发布于

2018-02-07

更新于

2022-04-24

许可协议

评论