Spring的IOC容器---BeanFactory---容器加载过程解析
容器加载
DefaultListableBeanFactory类介绍
DefaultListableBeanFactory类是BeanFactory的默认实现类,其间接实现BeanFactory接口、BeanDefinitionRegistry接口,同时依赖于BeanDefinition接口。其类图如下:
如上图所示,DefaultListableBeanFactory类间接实现BeanFactory接口和BeanDefinitionRegistry接口,其中BeanDefinitionRegistry接口又依赖于BeanDefinition接口。接下来将围绕着张图来讲解BeanFactory容器的启动过程。
BeanFactory接口介绍
BeanFactory接口主要是用于创建bean的工厂接口,其定义的接口方法分为以下几个部分:获取bean实例、判断容器中是否包含某个bean、判断实例是否为单列模式或则原型模式、类型匹配、实例类型、获取别名。具体接口内容如下:
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";
//获取bean实例
Object getBean(String var1) throws BeansException;
<T> T getBean(String var1, Class<T> var2) throws BeansException;
<T> T getBean(Class<T> var1) throws BeansException;
Object getBean(String var1, Object... var2) throws BeansException;
<T> T getBean(Class<T> var1, Object... var2) throws BeansException;
//是否包含指定实例
boolean containsBean(String var1);
//是否为单例模式
boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;
//是否为原型模式
boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;
//类型匹配
boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;
//返回实例类型
Class<?> getType(String var1) throws NoSuchBeanDefinitionException;
//返回别名
String[] getAliases(String var1);
}
BeanDefinition接口介绍
根据接口名称便可以猜出,该接口是跟bean定义相关。的确,该接口是用来管理每个bean的定义信息。在容器启动的时候,spring会将配置文件中定以的信息或则使用注解(如@Componet @Service @Controller等)定义的bean封装成一个个BeanDefinition对象,每个bean对应一个BeanDefinition对象,在对象实例化期间,使用bean对应的BeanDenifition对象通过反射生成实例对象。BeanDefinition接口方法用来管理bean的属性信息
,如父类名、类名、作用域、懒加载等。以下代码为BeanDefinition接口源码:
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
String SCOPE_SINGLETON = "singleton";
String SCOPE_PROTOTYPE = "prototype";
int ROLE_APPLICATION = 0;
int ROLE_SUPPORT = 1;
int ROLE_INFRASTRUCTURE = 2;
//父类名
void setParentName(String var1);
String getParentName();
//类名
void setBeanClassName(String var1);
String getBeanClassName();
//作用域
void setScope(String var1);
String getScope();
//懒加载
void setLazyInit(boolean var1);
boolean isLazyInit();
//依赖
void setDependsOn(String... var1);
String[] getDependsOn();
void setAutowireCandidate(boolean var1);
boolean isAutowireCandidate();
void setPrimary(boolean var1);
boolean isPrimary();
//工厂类名
void setFactoryBeanName(String var1);
String getFactoryBeanName();
//工厂方法名
void setFactoryMethodName(String var1);
String getFactoryMethodName();
ConstructorArgumentValues getConstructorArgumentValues();
MutablePropertyValues getPropertyValues();
boolean isSingleton();
boolean isPrototype();
boolean isAbstract();
int getRole();
String getDescription();
String getResourceDescription();
BeanDefinition getOriginatingBeanDefinition();
}
可以发现,BeanDefinition接口中的属性信息跟xml配置文件中的标签中的属性一一对应,也就是说一个BeanDefinition对应一个标签信息。在容器启动的时候,就会先将标签信息解析封装成BeanDefinition接口实现类对象。
BeanDefinition有三个间接实现类,分别是RootBeanFactory、ChildBeanFactory和GenericBeanDefinition。其中RootBeanFactory是用来封装父类定义信息(没有显示继承其他类的类)、ChildBeanDefinition是用来封装子类定义信息。
BeanDefinitionRegistry接口介绍
同样,根据接口名就可以看出,这个接口跟BeanDefinition对象的注册有关,上一部分将了,在容器启动阶段会将标签定义的bean信息解析封装成BeanDefinition对象,以备后续实例化使用,但是这些BeanDefinition归谁保存维护呢。没错,BeanDefinitionRegistry就是用来保存维护BeanDefinition对象的。
public interface BeanDefinitionRegistry extends AliasRegistry {
//注册BeanDefinition
void registerBeanDefinition(String var1, BeanDefinition var2) throws BeanDefinitionStoreException;
//移除BeanDefinition
void removeBeanDefinition(String var1) throws NoSuchBeanDefinitionException;
//获取BeanDefinition
BeanDefinition getBeanDefinition(String var1) throws NoSuchBeanDefinitionException;
//是否包含指定BeanDefinition对象
boolean containsBeanDefinition(String var1);
//获取所有BeanDefinition对象名称
String[] getBeanDefinitionNames();
//获取BeanDefinition对象数量
int getBeanDefinitionCount();
//BeanDefinition是否在使用
boolean isBeanNameInUse(String var1);
}
如何解析xml文件
到这里,大家想必对BeanDefinition管理和BeanFactory工厂有一定 的了解了吧,但是大家是否会疑惑,spring是如何将x在这里插入代码片
ml中的字符定义转换成BeanDefinition对象的呢,那么接下来我将带着大家继续探讨(其实看到这里,各位可以返现,我明没有深入的去分析源码,而是从原理层面上分析,当大家能够掌握基本原理再去看源码就应该简单了)
将解析之前我先上一段代码,如下:
public class ClassA {
public void say(){
System.out.println("我是ClassA对象,我在说话");
}
}
这是一个普通的class类
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="classA" class="springBeanTest.ClassA"/>
</beans>
这是spring的配置文件spring.xml
public class LifeCycleTest {
@Test
public void test1(){
//创建bean的工厂类
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//创建一个xml文件的reader类,将bean工厂传入reader中
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader((BeanDefinitionRegistry) beanFactory);
//加载xml未配置文件,并将配置文件解析成BeanDefinition对象保存在工厂类中
reader.loadBeanDefinitions("classpath:/spring.xml");
//从工厂类中获取classA类的beanDefinition对象
BeanDefinition beanDefinition = beanFactory.getBeanDefinition("classA");
//打印
System.out.println(beanDefinition.getBeanClassName());
System.out.println(beanDefinition.isSingleton());
}
}
运行结果如下图,可以看出,spring确实将xml配置文件解析成beanDefinition对象
- 以baenFactory为构造器参数,创建BeanDefinitionReader对象:
AbstractBeanDefinitionReader抽象类构造器部分内容:
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
//将beanFactory传给父类构造器
super(registry);
}
父类构造器部分代码如下:
protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
//父类构造器将beanFactory复制给自己的依赖对象,这样便为BeanDefinitionReader对象设置好了BeanFactory实例,用于注册beanDefinition。
this.registry = registry;
//初始化ResourceLoader
if (this.registry instanceof ResourceLoader) {
this.resourceLoader = (ResourceLoader)this.registry;
} else {
//创建resourceLoader对象,类型为DefaultResourceLoader
this.resourceLoader = new PathMatchingResourcePatternResolver();
}
}
- 调用AbstrcatBeanDefinitionReader方法:
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
//获取上一步初始化时创建的resourceLoader对象
ResourceLoader resourceLoader = this.getResourceLoader();
if (resourceLoader == null) {
//异常处理代码
} else {
int loadCount;
//接下来的代码是根据loaction的格式创建不同的Resource对象。
if (!(resourceLoader instanceof ResourcePatternResolver)) {
//省略部分代码
} else {
try {
//根绝loaction格式,创建像一面的Resource对象
Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location);
//从resource对象中加载beanDefinition对象
loadCount = this.loadBeanDefinitions(resources);
//省略部分代码
return loadCount;
} catch (IOException var10) {
//省略部分代码
}
}
}
}
Resource接口屏蔽了不同资源的访问差别,为上层提供统一的资源访问接口。
- 获取到resource对象之后,spring会使用该对象创建一个EncodedResource对象,主要是方便编码处理
以下代码是XmlBeanDefinitionReader类中的方法
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return this.loadBeanDefinitions(new EncodedResource(resource));
}
- 得到EncodedResource对象之后,调用以下方法进行xml文件解析,并封装成beanDefinition对象,储存在beanFactroy中。
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
//省去异常处理
Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!((Set)currentResources).add(encodedResource)) {
throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
} else {
int var5;
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//此处开始进行解析加载
var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
} finally {
inputStream.close();
}
} catch (IOException var15) {
//异常处理
} finally {
((Set)currentResources).remove(encodedResource);
if (((Set)currentResources).isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
return var5;
}
}
- doLoadBeanDefinitions方法源码如下:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
try {
//这里将从resource中加载资源,并封装成Document对象
Document doc = this.doLoadDocument(inputSource, resource);
//解析doucument对象,生成beanDefinition对象并注册到BeanDefinitionRegistry中(BeanFactory接口的默认实现类同时也实现了BeanDefinitionRegistry接口,因此可以从BeanDefinitionRegistry获取所有的beanDefinition对象)
return this.registerBeanDefinitions(doc, resource);
} catch (BeanDefinitionStoreException var4) {
//连续一串的异常处理
}
}
- registerBeanDefinitions方法,该方法的作用是使用documentReader对象读取document对象中的内容,并解析生成beanDefinition对象,然后注册到BeanDefinitionRegistry中去。详解解析过程此处不再继续深究。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//创建documentReader对象
BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
int countBefore = this.getRegistry().getBeanDefinitionCount();
//解析document对象,生成beanDefinition对象并注册
documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
return this.getRegistry().getBeanDefinitionCount() - countBefore;
}
- 总结:从以上步骤可以发现,xml的具体解析工作并非由BeanDefinitionReader对象来做的,而是由DocumentReader对象来完成的。下面是一张XmlBeanDefinitionReader类的类图,将根据这张类图来梳理整个流程。
BeanDefinitionReader类更具配置文件路径名格式,生成不同的Resource接口实现类,这样做的目的是屏蔽对不同资源加载的差异性,获得resource对象之后,BeanDefinitionReader使用resource对象,创建一个EncodedResource对象,方便对资源的编码处理。然后BeanDefinitionReader在解析EncodedResource对象,生成Doucement对象,最后才是使用BeanDefinitionReader内部的DoucementReader对象来解析Doucement,从而生成BeanDefinition对象,并注册到BeanDefinitionRegistry中去。至此,完成了配置文件的解析工作。这里说明一下AbstractBeanDefinitionReader的实现类不只XmlBeanDefinitionReader一个,还有PropertiesBeanDefinitionReader,不同的配置文件使用不同的实现类,同时也可以根据自己的需求自定义一个BeanDefinitionReader实现类,来完成配置文件的机械加载工作。