前言
这篇文章主要借鉴了一位大佬的文章,提炼了一下里面的内容,如果想更深入的学习可以:
点击这里
开始
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.11.RELEASE</version>
</dependency>
先用maven引入该依赖。
由上图可知我们要的只是核心容器的功能,那么只要引入spring的context就行了。context是什么?其实只要把它看成是一个全局的以beanName为key,beanDefinition为value的HashMap就行了,通过beanName来取beanDefinition。
先上一段简单代码:
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationfile.xml");
}
这其实就是把ApplicationContext读取classpath(src目录下以classpath指定名称)的xml配置文件,完成这一步之后就可以将bean实例化。具体可以再看一段代码:
public class App {
public static void main(String[] args) {
// 用我们的配置文件来启动一个 ApplicationContext
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:application.xml");
System.out.println("context 启动成功");
// 从 context 中取出我们的 Bean,而不是用 new MessageServiceImpl() 这种方式
MessageService messageService = context.getBean(MessageService.class);
// 这句将输出: hello world
System.out.println(messageService.getMessage());
}
}
此messageService是在xml中配置过的,所以可以实现getBean的功能。
BeanFactory
上文中所提及的ApplicationContext其实就是BeanFactory的一个派生接口
讲解一下ApplicationContext继承的两个类以及两个重要的类:
- ListableBeanFactory:Listable 的意思就是,通过这个接口,我们可以获取多个 Bean,而BeanFactory这个顶层接口只能够获得单个Bean。
- HierarchicalBeanFactory:可以在应用中启动多个BeanFactory,可以将他们设置为父子关系。
- AutowireCapableBeanFactory 这个名字中的 Autowire 大家都非常熟悉,它就是用来自动装配 Bean 用的,但是仔细看上图,ApplicationContext 并没有继承它,不过不用担心,不使用继承,不代表不可以使用组合,如果你看到 ApplicationContext 接口定义中的最后一个方法 getAutowireCapableBeanFactory() 就知道了。
- ConfigurableListableBeanFactory 也是一个特殊的接口,看图,特殊之处在于它继承了第二层所有的三个接口,而 ApplicationContext 没有。这点之后会用到。
以上四个类是最重要的4个类,接下来将要用到。
要研究ClassPathXmlApplicationContext就要先研究其构造方法:
其实仔细看了就发现除了无参构造和
//如果已经有 ApplicationContext 并需要配置成父子关系,那么调用这个构造方法
public ClassPathXmlApplicationContext(ApplicationContext parent) {
super(parent);
}
,其他构造都是调用了:
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent);
//根据提供的路径,处理成配置文件数组(以分号、逗号、空格、tab、换行符分割)
setConfigLocations(configLocations);
if (refresh) {
//refresh();是核心方法
refresh();
}
}
以及
public ClassPathXmlApplicationContext(String[] paths, Class<?> clazz, ApplicationContext parent)
throws BeansException {
super(parent);
Assert.notNull(paths, "Path array must not be null");
Assert.notNull(clazz, "Class argument must not be null");
this.configResources = new Resource[paths.length];
for (int i = 0; i < paths.length; i++) {
this.configResources[i] = new ClassPathResource(paths[i], clazz);
}
refresh();
}
我们主要来看第一个。
refresh()是可以销毁原application并重建(重新初始化)的f方法,下面是refresh()的具体实现;
@Override
public void refresh() throws BeansException, IllegalStateException {
// 来个锁,不然 refresh() 还没结束,你又来个启动或销毁容器的操作,那不就乱套了嘛
synchronized (this.startupShutdownMonitor) {
prepareRefresh();
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
try {
postProcessBeanFactory(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);
registerBeanPostProcessors(beanFactory);
initMessageSource();
initApplicationEventMulticaster();
onRefresh();
registerListeners();
finishBeanFactoryInitialization(beanFactory);
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
//销毁已经实例化的beans,防止占着资源
destroyBeans();
// Reset 'active' flag
cancelRefresh(ex);
// 把异常往外抛
throw ex;
}
finally {
resetCommonCaches();
}
}
}
- synchronized (this.startupShutdownMonitor) :通过同步锁来防止一个ApplicationContext还没有被初始化成功就被销毁的情况。
- prepareRefresh():准备refresh,记录下容器的启动时间、标记“已启动”状态、处理配置文件中的占位符
- ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory():这步显而易见的获得了一个beanFactory,讲未初始化的bean注册到了beanFactory
- prepareBeanFactory(beanFactory):设置 BeanFactory 的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean,用来加载类。
- rocessBeanFactory(beanFactory):这里是提供给子类的扩展点,到这里的时候,所有的 Bean 都加载、注册完成了,但是都还没有初始化
- invokeBeanFactoryPostProcessors(beanFactory):调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 方法
- registerBeanPostProcessors(beanFactory):注册 BeanPostProcessor 的实现类,注意看和 BeanFactoryPostProcessor 的区别,此接口两个方法: postProcessBeforeInitialization 和 postProcessAfterInitialization,两个方法分别在 Bean 初始化之前和初始化之后得到执行。注意,到这里 Bean 还没初始化
- initMessageSource():初始化当前 ApplicationContext 的 MessageSource
- initApplicationEventMulticaster():初始化当前 ApplicationContext 的事件广播器
- onRefresh():具体的子类可以在这里初始化一些特殊的 Bean
- registerListeners():注册事件监听器,监听器需要实现 ApplicationListener 接口
- finishBeanFactoryInitialization(beanFactory):初始化除了懒加载的类之外的所有的 singleton beans
总结
ApplicationContext虽然继承自BeanFactory,但是它真正用到的factory却是内部生成的DefaultListableBeanFactory的实例,而DefaultListableBeanFactory在上面的UML图中可以看到是最最底层,且实现最多的一个类,所以选择他肯定是功能最强大的。说到底ApplicationContext与BeanFactory相关的操作都是靠内部的beanFactory来实现的。
BeanDefinition
该接口的中文翻译是Bean定义,我们知道所有实例化的bean都会保存在beanFactory中,但是保存过程是怎么样的呢?其实就是把bean转化成BeanDefinition保存在beanFactory中,而beanDefinition则保存了bean的指向的是哪个类、是否是单例的、是否懒加载、这个 Bean 依赖了哪些 Bean 等等信息。然而这里虽然保存了这些信息,却没有保存我们所需要的类的实例,实例的具体去向在下文交代。
customizeBeanFactory 和 loadBeanDefinitions
customizeBeanFactory
customizeBeanFactory(beanFactory) 就是配置是否允许 BeanDefinition 覆盖、是否允许循环引用。
是否允许覆盖:就是当不同配置文件的bean有着相同id的时候,是报错还是让后者覆盖前者,默认是允许的
循环引用也很好理解:A 依赖 B,而 B 依赖 A。或 A 依赖 B,B 依赖 C,而 C 依赖 A。
默认情况下,Spring 允许循环依赖,当然如果你在 A 的构造方法中依赖 B,在 B 的构造方法中依赖 A 是不行的。
loadBeanDefinitions
/** 我们可以看到,此方法将通过一个 XmlBeanDefinitionReader 实例来加载各个 Bean。*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 给这个 BeanFactory 实例化一个 XmlBeanDefinitionReader
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 初始化 BeanDefinitionReader,其实这个是提供给子类覆写的,
// 我看了一下,没有类覆写这个方法,我们姑且当做不重要吧
initBeanDefinitionReader(beanDefinitionReader);
// 重点来了,继续往下
loadBeanDefinitions(beanDefinitionReader);
}
这个代码其实就是一个字符输入流将xml中对于BeanDefinition的信息读取,然后通过loadBeanDefinitions就是加载的过程了,通过配置文件获取了所有需要的信息,完成了Bean的加载,成为了BeanDefinition。beanDefinitionMap就是存储我们所需要BeanDefinition的地方,用beanName映射BeanDefinition。
以上就讲完了obtainFreshBeanFactory的作用。
prepareBeanFactory
prepareBeanFactory(factory)主要用于生成类加载器,添加几个 BeanPostProcessor顺便手动注册几个bean。
方法名叫做准备bean工厂,而他生成了类加载器就是因为类需要去加载,而几个手动注册的bean是有特殊用处的。
初始化所有singleton beans
触发所有非懒加载的singleton beans。这里需要注意的是在初始化的时候是会合并父bean的配置的,父bean就属于属性的继承,有点类似Java的继承,很好理解,如果不懂可以看原文的附录,有详细的介绍。其实要注意配置了abstract = true的singletons是不用加载的,就像抽象类是无法实现的一样,他们只是为子类提供一些统一配置的。如果是FactoryBean的话还要在它的命名之前加上一个"&"符号作为标识。
对于普通的bean,当调用getBean(beanName);的时候就完成了初始化
getBean(beanName)
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
// 我们在剖析初始化 Bean 的过程,但是 getBean 方法我们经常是用来从容器中获取 Bean 用的,注意切换思路,
// 已经初始化过了就从容器中直接返回,否则就先初始化再返回
@SuppressWarnings("unchecked")
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
// 获取一个 “正统的” beanName,处理两种情况,一个是前面说的 FactoryBean(前面带 ‘&’),
// 一个是别名问题,因为这个方法是 getBean,获取 Bean 用的,你要是传一个别名进来,是完全可以的
final String beanName = transformedBeanName(name);
// 注意跟着这个,这个是返回值
Object bean;
// 检查下是不是已经创建过了
Object sharedInstance = getSingleton(beanName);
// 这里说下 args 呗,虽然看上去一点不重要。前面我们一路进来的时候都是 getBean(beanName),
// 所以 args 其实是 null 的,但是如果 args 不为空的时候,那么意味着调用方不是希望获取 Bean,而是创建 Bean
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("...");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
// 下面这个方法:如果是普通 Bean 的话,直接返回 sharedInstance,
// 如果是 FactoryBean 的话,返回它创建的那个实例对象
// (FactoryBean 知识,读者若不清楚请移步附录)
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
if (isPrototypeCurrentlyInCreation(beanName)) {
// 当前线程已经创建过了此 beanName 的 prototype 类型的 bean,那么抛异常
throw new BeanCurrentlyInCreationException(beanName);
}
// 检查一下这个 BeanDefinition 在容器中是否存在
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// 如果当前容器不存在这个 BeanDefinition,试试父容器中有没有
String nameToLookup = originalBeanName(name);
if (args != null) {
// 返回父容器的查询结果
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
if (!typeCheckOnly) {
// typeCheckOnly 为 false,将当前 beanName 放入一个 alreadyCreated 的 Set 集合中。
markBeanAsCreated(beanName);
}
/*
* 稍稍总结一下:
* 到这里的话,要准备创建 Bean 了,对于 singleton 的 Bean 来说,容器中还没创建过此 Bean;
* 对于 prototype 的 Bean 来说,本来就是要创建一个新的 Bean。
*/
try {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// 先初始化依赖的所有 Bean,这个很好理解。
// 注意,这里的依赖指的是 depends-on 中定义的依赖
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
// 检查是不是有循环依赖,这里的循环依赖和我们前面说的循环依赖又不一样,这里肯定是不允许出现的,不然要乱套了,读者想一下就知道了
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
// 注册一下依赖关系
registerDependentBean(dep, beanName);
// 先初始化被依赖项
getBean(dep);
}
}
// 创建 singleton 的实例
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
// 执行创建 Bean,详情后面再说
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// 创建 prototype 的实例
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
// 执行创建 Bean
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
// 如果不是 singleton 和 prototype 的话,需要委托给相应的实现类来处理
else {
String scopeName = mbd.getScope();
final 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>() {
@Override
public Object getObject() throws BeansException {
beforePrototypeCreation(beanName);
try {
// 执行创建 Bean
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
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",
ex);
}
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
// 最后,检查一下类型对不对,不对的话就抛异常,对的话就返回了
if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {
try {
return getTypeConverter().convertIfNecessary(bean, requiredType);
}
catch (TypeMismatchException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
return (T) bean;
}
我再简化一下上述过程:首先先校验一下beanName,无论是BeanFactory还是别名都可以最终转化为“正统”的beanName。之后就是检查是否已经实例化过了该singletons,如果已经创建过了,会根据不同的情况有不同的做法,如果未创建过或者是prototype的话就是创建一个新的bean,注意在初始化该实例之前要先初始化所有它的依赖项,其实也很好理解,要生出你这个人的之前肯定要先生出手脚之类的,否则没有这些器官的“你”还是你么?但是这时的依赖关系仅存在于从配置读取的String[] dependsOn之中,当初始化这些实例之后还要注册到管理依赖关系的Map之中,之后就可以初始化被依赖项了,相当于该实例已经创建,现在注入到该实例中,其实仔细看代码这就是一种递归的关系。初始化实例的时候还要根据其具体的情况来进行实例化的过程,系统只定义了singleton和prototype两种情况,其他情况要靠自己扩展了。当我们创建完实例之后再去校验一下实例的类型,不对的话就抛异常,对的话就可以返回该实例了。实例化过程还设计参数设置等复杂过程,但是很多代码一时半会也无法完全理解,待日后再来细品。
总结
其实spring的Ioc就是把要管理的bean放入一个Map容器,以它的唯一标识beanName映射到bean的所有配置beanDefinition,之后就是根据Map的中的配置在spring容器启动的时候加载所有非懒加载的实例,这就是Ioc原理的大概内容。