(一)Spring 容器及 Spring Bean
1.Spring 容器
1.1 什么是容器
官网中有一句话
The org.springframework.context.ApplicationContext interface represents the Spring IoC container and is responsible for instantiating, configuring, and assembling the beans.
翻译下来的意思是:
- Spring IOC 容器就是一个 org.springframework.context.ApplicationContext 的实例化对象
- 容器负责实例化,配置以及装配 bean
那么我们可以这样理解:
- 从代码层次来看,Spring 容器就是一个实现了 ApplicationContext 接口的对象
- 从功能上来看,Spring 容器是 Spring 框架的核心,用来管理对象的。容器将创建的对象,把它们连接在一起,配置它们,并管理它们的整个生命周期从创建到销毁
1.2 容器如何工作
官网上一张图如下:
图可以理解为,Spring 容器通过我们提交的 POJO 对象(Your Business Objects(POJOs))以及配置元数据(Configuration Metadata)产生一个充分配置的可以使用的系统
这里说的配置元数据,实际上就是我们提供的 XML 配置文件,或者通过注解方式提供的一些配置信息
2.Spring bean
2.1 如何实例化一个 bean
从官网上来看,主要有三种方法:
- 构造方法
- 静态工厂方法
- 实例工厂方法
这三种例子官网都有具体的演示,可以通过注解查阅部分源码来验证官网的结论
从代码角度进行分析,这里我们直接定位到
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance
通过运行下面这行代码
public
在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance 这个方法的入口打一个端点,如图:
接下来我们通过代码来分析
我们主要关注进行实例化的几个方法:
- 通过 Supplier 创建 Bean
通过 BeanDefinition 中的 instanceSupplier 直接获取一个是实例化的对象,这个 instanceSupplier 属性我不是很理解,在 XML 标签中以及注解的方式都没有找到方法配置这个属性,后来在 org.springframework.context.support.GenericApplicationContext 这个类中找到了以下两个方法
经过断点测试,发现这种情况下,在实例化对象时会进入上面的 supplier 方法。下面是测试代码
// AnnotationConfigApplicationContext 是 GenericApplicationContext 的一个子类
这个方法一般不常用,这是 Spring 提供的一种方便外部扩展的手段,让开发者能够更加灵活的实例化一个 bean
2. 通过注解的方式来创建 Bean
@Compent,@Service 等注解方式
测试代码:
public
观察 debug:
可以发现,代码执行到最后一行,同时我们看代码上面的注释可以知道,当没有进行特殊处理的时候,默认会使用无参构造函数进行对象的实例化
3. 通过普通 XML 的方式
同@Component 注解
4. 通过@Configuration 注解的方式
public
同样,断点也进入了最后一行
5. 通过@Bean 方式
public
在方法入口打个断点如图:
断点结果:
可以发现,通过@Bean方法创建对象时,Spring底层是通过factoryMethod方法进行实例化对象的,Spring会在我们需要实例化这个对象对应的BeanDefinition中记录factoryBeanName是什么(factoryBeanName就是AppConfig),同时记录这个factoryBean中创建对象的factoryMethodName是什么,最后通过factoryBeanName获取一个Bean然后反射调用factoryMethod实例化一个对象。
这里需要注意几个概念:
- 这里所说的通过静态工厂方式通过factoryBeanName获取一个Bean,注意这个Bean,不是一个FactoryBean,也就是说不是一个实现了org.springframework.beans.factory.FacrotyBean接口的Bean。
- 提到一个概念BeanDefinition,它就是Spring对自己所管理的Bean的一个抽象。之后会出一个专题专门讲解
6. 通过静态工厂方法的方式
测试代码:
public
application.xml:
<?
断点如下:
可以发现,这种情况也进入了insrantiateUsingFactoryMethod方法中。通过静态工厂方法的方式特殊之处在于,包含了这个静态方法的类,不需要被实例化,不需要被Spring管理,Spring的调用逻辑大概是:
- 通过<bean>标签中的class属性得到一个Class对象
- 通过Class对象获取到对应的方法名称得到Method对象
- 最后反射调用Method.invoke(null, args)
因为是静态方法,方法在执行时,不需要一个对象
7.通过实例工厂方法的方式
测试代码,配置文件不变:
ClassPathXmlApplicationContext
断点如下:
还是执行这个方法,这个方法的执行过程和@Bean方式执行的流程是一样的
到这里,这段代码我们算是结合了官网大致过了一遍,其实还遗留了几个问题:
- Spring是如何推断构造函数的?我们在上面验证的都是无参的构造函数,并且只提供了一个构造函数
- Spring是如何推断方法的?不管是静态工厂方法还是实例化工厂方法,我们都只在类中提供了一个和配置匹配的方法名,假设我们对方法进行了重载呢?
要说清楚这两个问题需要比较深入的研究代码,同时进行测试。
总结
- 对象实例化,只是得到一个对象,还不是一个完全的Spring中的bean,我们实例化后的这个对象还没有完成依赖注入,没有走完一系列的生命周期
- Spring官网上说到,在Spring实例化一个对象有三种方式:
构造函数
实例化工厂方法
静态工厂方法
3.自己总结如下结论:
Spring通过解析我们的配置元数据,以及我们提供的类对象得到一个BeanDefiniton对象,通过这个对象可以实例化一个java bean对象。主要流程图如下:
不吃竹子的滚滚:Spring源码分析(十四)Spring中的BeanWrapper及类型转换
不吃竹子的滚滚:Spring源码分析(十二)ApplicationContext详解(中)
不吃竹子的滚滚:Spring源码分析(十一)ApplicationContext详细介绍(上)
不吃竹子的滚滚:Spring源码分析(十)Spring中Bean的生命周期(下)
不吃竹子的滚滚:Spring源码分析(九)Spring中Bean的生命周期(上)
不吃竹子的滚滚:Spring源码分析(八)容器的扩展点(BeanPostProcessor)
不吃竹子的滚滚:Spring源码分析(七)容器的扩展点(FactoryBean)
不吃竹子的滚滚:Spring源码分析(六)容器的扩展点(BeanFactoryPostProcessor)
不吃竹子的滚滚:Spring源码分析(五)BeanDefinition(下)
不吃竹子的滚滚:Spring源码分析(四)BeanDefinition(上)
不吃竹子的滚滚:Spring源码分析(三)自动注入与精确注入
不吃竹子的滚滚:Spring源码分析(二)依赖注入及方法注入
不吃竹子的滚滚:Spring源码分析(一)Spring容器及Spring Bean