目录
1.1 BeanDefinition的Resource的定位
IOC容器系列包含BeanFactory和ApplicationContext,这两个接口就是IOC的具体表现形式。
他们的接口关系设计图如下所示:
主要接口设计主线:
- 1.BeanFactory -> HierarchicalBeanFactory->ConfigurableBeanFacotry
- 2.BeanFactory -> ListableBeanFactory -> ApplicationContext -> WebApplicationContext/ConfigurableApplicationContext
我们常用的应用上下文就是WebApplicationContext/ConfigurableApplicationContext的实现。
BeanFactory
DefaultListableBeanFactory这个基本IOC容器的实现就是实现了ConfigurableBeanFactory,而其他的都是在这个类上做扩展。
一般在使用IOC容器的时候,需要如下几个步骤:
1.创建IOC配置的抽象资源,这个抽象资源包含了BeanDefinition的定义信息
2.创建一个BeanFactory,比如DefaultListableBeanFactory
3.创建一个载入BeanDefinition的读取器,例如XmlBeanDefinitionReader来载入xml文件形式的BeanDefinition,通过一个回调配置给BeanFactory
4.从定义好的资源位置读入配置信息,具体的解析过程就交给XMLBeanDefinitionReader来完成,完整的在和注册Bean定义之后,需要的Ioc容器就建立好了,这时候就可以使用Ioc容器了
比如我们之前用的XmlBeanFactory,现在已经废弃使用了。
ClassPathResource classPathResource = new ClassPathResource("beans.xml"); DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); beanDefinitionReader.loadBeanDefinitions(classPathResource);
ApplicationContext
相比使用BeanFactory,大家更偏向使用功能更加强大的ApplicationContext,他在BeanFactory的基础上添加了很多BeanFactory不具备的功能,
1)支持不同的消息源。ApplicationContext扩展了MessageSource接口,这是信息源的扩展能工可以支持和国际化的实现,为开发多语言版本的应用提供了服务
2) 访问资源,我们对于ResourceReader和Resource的支持,可以从多个角度获取资源。
3) 支持应用实践。继承了接口ApplicationEventPubliser,从而在上下中引入了时间机制,这些事件和Bean的生命周期的结合为Bean的管理提供了遍历
4)在ApplicationContext中提供了附加服务,是的ApplicationContext更像面向框架的使用风格。建议开发中使用ApplicationContext作为Ioc容器的基本形式。
例如:ClassPathXmlApplicationContext和FileSystemXmlApplicationContext都是抽象类AbstractXmlApplicationContext的实现,都是从xml配置文件中获取资源配置bean,前者是从类路径获取配置文件,后者是通过文件系统载入。
这两个类都是都是根据不同的形式读取resource,都是重写了抽象父类的getConfigResources模板方法。如下:
// FileSystemXmlApplicationContext这个类中的,其他也都是构造方法,都是不同的入参调用下面这个方法 ,Class
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
super(parent);
this.setConfigLocations(configLocations);
if (refresh) {
this.refresh(); //这是启动整个IOC 的核心方法
}
}
//这个是读取资源,封装为fileSystem资源,这里重写了父类的模板方法
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
1.IOC初始化过程
IOC容器的初始化都是如上面的refresh()来启动的,这个方法标志着IOC容器的正式启动,这个启动包含了BeanDefinitionResource的定位,载入和注册三个基本过程。这里把这几个过程分开做模块就是做到解耦,降低组件的耦合性。
- 1)Resource定位过程,这个指的就是BeanDefinition(bean在IOC中的抽象存储)资源的定位,它由ResourceLoader通过统一的Resource接口来完成。
- 2) BeanDefinition的载入,就是把用户定义好的Bean表示为IOC容器内部的数据结构,而这个容器的内部结构就是BeanDefinition。简单说BeanDefinition就是POJO对象在IOC容器中的抽象,通过这个BeanDefinition定义的数据结构,是Ioc容器能够方便对POJO对象进行管理。
- 3) 向IOC容器中注册这些BeanDefinition的过程,这个过程就是通过BeanDefinitionRegistry接口来完成的。这个注册过程把载入过程解析得到的BeanDefinition向IOC容器进行注册。内部就是将BeanDefinition注入到一个HashMap中去,IOC容器就是通过这个HashMap来持有这些BeanDefinition数据的。
注意点:IOC容器的初始化不包含Bean的依赖注入。Bean定义的载入和依赖注入是分开的两个过程。依赖注入一般发生在应用第一次通过getBean向容器索取Bean的时候。不过我们可以通过设置lazyinit属性,让容器在初始化的时候就让这个Bean做到依赖注入,而不用等到第一次getBean的时候调用
1.1 BeanDefinition的Resource的定位
Resource接口的实现类图如下:
例如:
ClassPathResource res = new ClassPathResource("bean.xml");
//表示从当前类路径去找寻以文件形式存在的BeanDefinition。
但是获取到的Resource并不能直接被BeanFactory使用,这里需要使用BeanDefinitionReader对于这些信息进行处理,而例如BeanFactory的一种重要实现-DefaultListableBeanFactory只是一个纯粹的Ioc容器,需要为他配置特定的读取器才能完成想用的读取功能,而ApplicationContext就提供了许多不同的Resource的读取器来完成读取。
下面来研究FileSystemXmlApplicationContext
1)结构图:
通过前面的ConfigurableApplicationContext就继承了所有ApplicationContext的所有特性,包含ResourceLoader读入和Resource定义BeanDefinition的能力。
2)这个类源码
public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {
public FileSystemXmlApplicationContext() {
}
public FileSystemXmlApplicationContext(ApplicationContext parent) {
super(parent);
}
public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
this(new String[]{configLocation}, true, (ApplicationContext)null);
}
//支持多个配置文件
public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {
this(configLocations, true, (ApplicationContext)null);
}
public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {
this(configLocations, true, parent);
}
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {
this(configLocations, refresh, (ApplicationContext)null);
}
//自己双亲IOC容器
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
super(parent);
this.setConfigLocations(configLocations);
if (refresh) {
this.refresh(); //调用refresh函数载入BeanDefinition。
}
}
//这个方法是在BeanDefinitionReader的loadBeanDefinition方法中被调用的,loadBeanDefinition这个方法采用的模板方法,由各个子类来完成的
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
}
3)重点方法refresh方法分析的调用情况
FileSystemXmlApplicationContext中的构造方法调用了的refresh方法调用了最后调用父类的父类的方法refreshBeanFactory,如下面的时序图。
refreshBeanFactory方法源码如下:
protected final void refreshBeanFactory() throws BeansException {
if (this.hasBeanFactory()) {
this.destroyBeans();
this.closeBeanFactory();
}
try {
//这里通过新建IOC容器。
DefaultListableBeanFactory beanFactory = this.createBeanFactory();
beanFactory.setSerializationId(this.getId());
this.customizeBeanFactory(beanFactory);
//这里是模板方法在AbstractBeanDefinitionReader中可以看到具体实现.这里载入BeanDefinition
this.loadBeanDefinitions(beanFactory);
Object var2 = this.beanFactoryMonitor;
synchronized(this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
} catch (IOException var5) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);
}
}
4)FileSystemXmlApplicationContext中的方法 getResourceByPath方法的调用关系图,上面的refresh方法会调用到FileSystemXmlApplicationContext自身的的这个方法:
1.2 BeanDefinition的载入和解析
在完成对BeanDefinition的Resource定位之后就需要了解BeanDefinition的载入过程,这个过程相当于把定义的BeanDefinition在IOC容器中转化成一个Spring内部表示的数据结果的过程。
1)在AbstractApplicationContext中被子类调用的refresh的方法源码:
public void refresh() throws BeansException, IllegalStateException {
Object var1 = this.startupShutdownMonitor;
synchronized(this.startupShutdownMonitor) {
this.prepareRefresh();
//启动子类中的refreshBeanFacotry,包含创建IOC容器
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory);//设置BeanFactory后置处理器
this.invokeBeanFactoryPostProcessors(beanFactory); //调用BeanFactory的后置处理器,这些后置处理器是在bean定义中想容器注册的
this.registerBeanPostProcessors(beanFactory);//注册bean的后置处理器,在Bean创建过程中调用
this.initMessageSource(); //初始化上下文中的消息源
this.initApplicationEventMulticaster();//初始化上下文中的事件机制
this.onRefresh();//这个没有被实现
this.registerListeners();//检查监听器并将这些bean注册
this.finishBeanFactoryInitialization(beanFactory);//实例化所有的non-lazy-init的bean
this.finishRefresh(); //发布容器事件,结束refresh过程
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches(); //
}
}
}
2)BeanDefinition解析的时序图
可以看到在AbstractXmlApplicationContext中的方法,可以采用XmlBeanDefinitionReader来载入BeanDefinition
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
this.initBeanDefinitionReader(beanDefinitionReader);
this.loadBeanDefinitions(beanDefinitionReader);
}
这里的loadBeanDefinitions方法最终是由XmlBeanDefinitionReader实现。最终处理后的结果由BeanDefinitionHolder来持有结果,除了BeanDefinition外,还包含bean的名字,别名集合,这个BeanDefinitionHolder的生成是通过对Document文档书的内容进行解析来完成的,可以知道这个解析过程是由BeanDefinitionParseDelegate来实现的,具体在processBeanDefinition中实现。
BeanDefinitionParseDelegate包含对Array,List,Set,Map,Prop等各种元素的解析,并生成相应的数据对象。我们可以以AbstractBeanDefintion为入口,让IOC容器执行索引,查询和操作。
1.3 BeanDefinition在IOC容器中的注册
我们对BeanDefinition进行载入和解析后,需要把它向IOC容器中注册,在DefaultLIstableBeanFactory中有一个map用来存储这个的
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap(256);
1)我们可以看到注册的调用过程如下:
2)registerBeanDefinition的方法调用链如下所示:
由于DefaultListableBeanFactory实现了BeanDefinition接口,只需要把这个BeanDefinition设置到hashMap中即可。完成了BeanDefinition的注册,那么就完成了IOC容器的初始化偶成,此时在使用IOC容器的DefaultListableBeanFactory中已经建立的整个Bean的配置信息,而且这些BeanDefinition已经可以被容器使用了。他们都是在beanDefinitionMap里被检索和使用。键名是beanName;
1.4 IOC容器的依赖注入
依赖注入都是在用户第一次向容器获取Bean的时候触发,但是我们也可以通过控制lazyinit属性对其预实例化。当我们获取Bean的时候是通过BeanFactory中的方法getBean方法获取。其中DefaultListableBeanFactory中的getBean方法都是调用基类中的AbstractBeanFactory中的getBean方法。
1)具体源码:
//这里是对BeanFactory接口的实现,比如getBean方法,而这个方法实际上又是调用的doGetBean来实现的
public AbstractBeanFactory(BeanFactory parentBeanFactory) {
this.parentBeanFactory = parentBeanFactory;
}
public Object getBean(String name) throws BeansException {
return this.doGetBean(name, (Class)null, (Object[])null, false);
}
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return this.doGetBean(name, requiredType, (Object[])null, false);
}
public Object getBean(String name, Object... args) throws BeansException {
return this.doGetBean(name, (Class)null, args, false);
}
public <T> T getBean(String name, Class<T> requiredType, Object... args) throws BeansException {
return this.doGetBean(name, requiredType, args, false);
}
//这里是实际取得Bean的地方,也是触发依赖注入发生的地方
protected <T> T doGetBean(String name, Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException {
final String beanName = this.transformedBeanName(name);
//先从缓存中获取单例的bean
Object sharedInstance = this.getSingleton(beanName);
Object bean;
if (sharedInstance != null && args == null) {
if (this.logger.isDebugEnabled()) {
if (this.isSingletonCurrentlyInCreation(beanName)) {
this.logger.debug("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference");
} else {
this.logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
//这里的完成的是FactoryBean的相关处理,以取得FactoryBean的生产结果
bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, (RootBeanDefinition)null);
} else {
if (this.isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
//利用双亲BeanFactory获取
BeanFactory parentBeanFactory = this.getParentBeanFactory();
if (parentBeanFactory != null && !this.containsBeanDefinition(beanName)) {
String nameToLookup = this.originalBeanName(name);
if (args != null) {
return parentBeanFactory.getBean(nameToLookup, args);
}
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
if (!typeCheckOnly) {
this.markBeanAsCreated(beanName);
}
try {
//通过Bean的名字获取BeanDefinition
final RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName);
this.checkMergedBeanDefinition(mbd, beanName, args);
//获取当前Bean的依赖项,这里触发递归调用,直到没有依赖为止
String[] dependsOn = mbd.getDependsOn();
String[] var11;
if (dependsOn != null) {
var11 = dependsOn;
int var12 = dependsOn.length;
for(int var13 = 0; var13 < var12; ++var13) {
String dep = var11[var13];
if (this.isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
this.registerDependentBean(dep, beanName);
this.getBean(dep);
}
}
//判断是单例的情况下,使用createBean方法创建bean
if (mbd.isSingleton()) {
sharedInstance = this.getSingleton(beanName, new ObjectFactory<Object>() {
public Object getObject() throws BeansException {
try {
return AbstractBeanFactory.this.createBean(beanName, mbd, args);
} catch (BeansException var2) {
AbstractBeanFactory.this.destroySingleton(beanName);
throw var2;
}
}
});
bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} else if (mbd.isPrototype()) { //这里创建prototype的bean
var11 = null;
Object prototypeInstance;
try {
this.beforePrototypeCreation(beanName);
prototypeInstance = this.createBean(beanName, mbd, args);
} finally {
this.afterPrototypeCreation(beanName);
}
bean = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
} else {
String scopeName = mbd.getScope();
Scope scope = (Scope)this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
public Object getObject() throws BeansException {
AbstractBeanFactory.this.beforePrototypeCreation(beanName);
Object var1;
try {
var1 = AbstractBeanFactory.this.createBean(beanName, mbd, args);
} finally {
AbstractBeanFactory.this.afterPrototypeCreation(beanName);
}
return var1;
}
});
bean = this.getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
} catch (IllegalStateException var21) {
throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton", var21);
}
}
} catch (BeansException var23) {
this.cleanupAfterBeanCreationFailure(beanName);
throw var23;
}
}
//最后对bean的类型检查,通过后就返回bean,这个bean包含依赖关系的bean
if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {
try {
return this.getTypeConverter().convertIfNecessary(bean, requiredType);
} catch (TypeMismatchException var22) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", var22);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
} else {
return bean;
}
}
2)getBean方法调用的时序图
重要方法是createBeanInstance和populateBean方法,这个生成bean的方式很多,可以通过工厂方式,也可以autowire,CCLIB实例化等。SimpleInstantiationStrategy类是用来生成bean对象的默认实现类,提供两种实例化java对象的方法,一种是通过BeanUtil(JVM反射技术),另外一个种就是CGLIB技术。后面通过BeanDefinitionResolver对BeanDefinition进行解析,然后注入到property中。
这里也基本都是摘录《Spring技术内幕》里面的内容。描述了一个大概的IOC内容,如有错误,欢迎指正。