上一篇把Spring学习的大纲大体列了下,几天过去了,不敢说自己忙(因为一说忙就是借口,时间都是挤出来的),今天趁双节休息时间,把我们Spring源码的整体脉络先补充下,也借此篇文章祝大家中秋国庆快乐。
Spring源码的整体脉络
一、从哪里开始入手学习
1、先知道的几个概念
每当在讲到什么是Spring时,我们一般会想到Spring是IOC和AOP的容器。
那么问题又来了,什么是IOC呢?
IOC是控制反转的意思,它是一种理念,用来解决层与层或者类与类之间的耦合问题,DI才是它的实现。
又冒出了一个概念,DI又是个啥呀?
DI顾名思义,依赖注入。
下面介绍一个例子,来理解下IOC和DI的原理。
在没有使用SpringIOC时,如果A类需要调用B类对象,一般我们会在A类里
ClassB classB = new ClassB()
对应这样的代码引用,当我们需要将ClassB换成ClassC时,需要改动这行代码,如果这样的引用有数十上百个,那么一来每个地方都去改工作量大,更重要的是不能保证不出问题,我们要知道任何小的代码改动都有可能引入BUG。
因此,通过SpringIOC的将控制权反转便能解决这个问题
@Autowried
IClassB classB;
一般将B类去实现一个接口,当我们需要将ClassB换成ClassC时,让C类也去实现这个接口,将C类设置为注入的类,这样的C类需要依赖其他类来注入,即依赖注入。
2、看源码入口
SpringIOC的加载是在Spring应用的上下文ApplicationContext开始的,所以我们在看源码前可以先新建一个入口类
public class testSpring {
ApplicationContext ioc = new ClassPathXmlApplicationContext("classpath:spring.xml");
//ApplicationContext ioc = new AnnotationConfigApplicationContext(IoCJacaConfig.class);
User user = (User)ioc.getBean("userProperty");//user
System.out.println(user);
}
二、SpringIOC的加载过程分析
1、从类到Bean
Ioc容器承载的是一堆bean,从类注入到容器成为Bean不是一步到位的,中间涉及到很多SpringIOC的加载环节,对于使用Spring的用户来说可以分为以下3个步骤。
- 配置类(xml、xml+@注解、配置类<@纯注解>)
- 加载Spring应用上下文
new ClassPathXmlApplicationContext(“xx.xml”);
new AnnotationConfigApplicationContext(xx.class); - 获取bean:getBean();
用流程图来大体表示下SpringIoc的加载流程,如下:
2、BeanFactory
先看一段代码
public Object getBean(String name) throws BeansException {
this.assertBeanFactoryActive();
return this.getBeanFactory().getBean(name);
}
这是AbstractApplication类实现了BeanFactory接口的一段代码,当然这可以不关心,这段代码主要想说明,我们调用getBean时是调用BeanFatory去获取的bean,因此这里引入一个Spring最核心、最重要的组件beanFactory,我们称之为bean工厂。beanFactory是Spring最核心的顶层接口,它只有一个唯一的职责生产bean。
加载Spring应用上下文时就是通过bean工厂来将类注入到容器中成为一个bean,上面我们提到bean工厂的职责是负责生产bean,那么它到底是怎么运作的呢?
beanFatory使用的是简单工厂模式,根据每个类封装好的一个“东西”(这个东西接下来会讲到)生成相应的bean。
讲到这里有个很重要的问题:BeanFactory和ApplicationContext的区别?
- 他们都有生产bean的能力,但Spring应用上下文它实际上不生产bean,它最终也是调用bean工厂来生产bean。另外,上下文可以通过扫描包或配置类一次同事装载多个bean的功能(更好地服务类),而bean工厂只能一个一个来生产。
- 他们都可以作为容器,但BeanFactory是简单的容器,只提供装载和获取bean的功能,ApplicationContext提供更强大的容器功能,例如国际化、AOP等;
- 区别:BeanFactory在启动的时候不会去实例化Bean,中有从容器中拿Bean的时候才会去实例化;ApplicationContext在启动的时候就把所有的Bean全部实例化了。它还可以为Bean配置lazy-init=true来让Bean延迟实例化;
- 选择:一般web应用都选择ApplicationContext,对资源要求较高的应用选择BeanFactory。
3、BeanDefinition
还记得上面在讲BeanFactory时提到的一个封装好的“东西”吗,这个东西就是BeanDefinition,我们称之为Bean定义,因为BeanFactory是只提供装载(生产)bean和获取bean的功能,它是一台没有感情的机器,对于xml、@注解或javaConfig的不同方式的类配置,Spring必须提前转化为一个统一的东西给BeanFactory他才能工作,所以就引出了BeanDefinition,它也是Spring顶层的核心接口,封装了生产bean的一切原料(懒加载、单例/多例等)。
每个类对应一个BeanDefinition,所有的BeanDefinition放在一个BeanDefinitionMap里,BeanFactory循环这个Map去实例化为bean对象。
类注册为BeanDefinition需要经过3个步骤:
- BeanDefinitionReader:读取配置类配置的扫描包
- BeanDefinitionScanner:在这些包内扫描所有的类
- BeanDefinitionRegistry:将扫描到的类注册为bean定义
BeanFatory调用getBean去生产bean,getBean既有生产功能又有获取功能,如果存在就返回,如果不存在就生产。
到此,Spring应用的上下文加载已大概完成,现在我们把上面的SpringIoc的加载流程稍作补充
三、Bean的生命周期
bean的生成不是一步到位的,需要经历3个过程:
- 实例化
- 填充属性
- 初始化
1、初始化的方式有两种,反射和工厂方法。反射是由Spring控制的,工厂方法可以自己控制new的过程(相对比较灵活);
2、实例化化后的bean只是一个空壳,需要经过属性填充,解析注解(如@Autowired @Value)
3、初始化,如initMetho、初始化结束方法。
在bean的生命周期过程中要提到2个很重要的知识点:
3.1、循环依赖
循环依赖的出现是在填充属性阶段。
class BeanA {
@Autowired
private BeanB b;
}
class BeanB {
@Autowired
private BeanA a;
}
对于这样的两个类,在beanA属性填充时,由于依赖beanB,则需要先生成beanB,而在beanB的属性填充时发现依赖beanA,由此变出现了循环依赖。Spring解决循环依赖的方式是使用三级缓存(后面文章会详细讲到)。
3.2、Aware
在bean的初始化阶段会调用一堆的aware,例如ApplicationContextAware ,它的用法是当实现了这个接口,必须实现一个方法,通过这个方法可以获得Spring应用上下文。
@Component
public class AppUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext arg0) throws BeansException {
applicationContext = arg0;
}
}
至此,bean的生命周期完成,最终将这些成熟的bean存放至一级缓存,Map<beanName,bean的实例>。
四、Spring扩展点
4.1、bean工厂的后置处理器
- BeanFactoryPostProcessor
修改:从bean工厂中获取beanDefinition去修改。 - BeanFactoryRegistryPostProcessor
注册:获取BeanDifinitionRegistry去添加bean定义。
- 在Spring集成Mybatis时就用到了这些扩展接口,因为Mapper是接口,没法注册为bean,因此通过BeanFactoryRegistryPostProcessor可以新增beanDefinition注册为bean;
bean工厂的后置处理器的扩展节点是属于ApplicationContext这个过程中的提供的,它也是ApplicationContext和BeanFatory的区别之一;
事实上BeanDefinitionReader\BeanDefinitionScanner\BeanDefinitionRegistry严格来说都是通过bean工厂的后置处理器集成的;
Spring生态除了IOC,其他都是通过扩展节点集成进来的。
4.2、bean的后置处理器
BeanPostProcessor是用来控制bean的生产过程,在整个bean的生命周期中共会调用9次。
猜想AOP是在哪个扩展点集成的?
事实上在实例化后就可以集成AOP,但是在倒数第二个(最后一个是销毁bean)才是真正的bean创建完成,为了实现彻底解耦,在这个扩展节点集成了AOP。
4.3、还有很多其他扩展点
小节: BeanFactoryPostProcessor和BeanPostProcessor都是ApplicationContext集成的(xxxApplicationContext继承了BeanFatory),没有这两个扩展点就没办法读取扫描配置类和解析@Autowried以及AOP等 到此为止,Spring源码的整体脉络基本差不多了,最后将流程图补充完整来总结下这个脉络。