Spring官网阅读(一)容器及实例化
从今天开始,我们一起过一遍Spring的官网,一边读,一边结合在路神课堂上学习的知识,讲一讲自己的理解。不管是之前关于动态代理的文章,还是读Spring的官网,都是为了之后对Spring的源码做更全面细致的学习,所以在这个过程中,不会涉及过多底层的代码,更多是通过例子证明我们在官网得出的结论,希望自己可以坚持下来,给自己加个油!!!
本文主要涉及到官网中的
1.2,1.3节。
Spring容器
容器是什么?
我们先看官网中的一句话:
The
org.springframework.context.ApplicationContextinterface 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 框架的核心,是用来管理对象的。容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁。
容器如何工作?
我们直接看官网上的一张图片,如下:

Spring容器通过我们提交的pojo类以及配置元数据产生一个充分配置的可以使用的系统
这里说的配置元数据,实际上我们就是我们提供的XML配置文件,或者通过注解方式提供的一些配置信息
Spring Bean
如何实例化一个Bean?
从官网上来看,主要有以下三种方法

- 构造方法
- 通过静态工厂方法
- 通过实例工厂方法
这三种例子,官网都有具体的演示,这里就不再贴了,我们通过自己查阅部分源码,来验证我们在官网得到的结论,然后通过debug等方式进行验证。
我们再从代码的角度进行一波分析,这里我们直接定位到org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance这个方法中,具体定位步骤不再演示了,大家可以通过形如下面这段代码:
ClassPathXmlApplicationContext cc =
// 这里我们通过xml配置实例化一个容器
new ClassPathXmlApplicationContext("classpath:application.xml");
MyServiceImpl luBan = (MyServiceImpl) cc.getBean("myServiceImpl");
直接main方法运行,然后在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance这个方法的入口打一个断点,如图:


接下来我们对这个方法进行分析,代码如下:
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// 1.获取这个bean的class属性,确保beanDefinition中beanClass属性已经完成解析
// 我们通过xml从<bean>标签中解析出来的class属性在刚刚开始的时候必定是个字符串
Class<?> beanClass = resolveBeanClass(mbd, beanName);
// 省略异常判断代码.....
// 2.通过beanDefinition中的supplier实例化这个bean
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
// 3.通过FactoryMethod实例化这个bean
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// 4.下面这段代码都是在通过构造函数实例化这个Bean,分两种情况,一种是通过默认的无参构造,一种 是通过推断出来的构造函数
boolean resolved = false;
boolean autowireNecessary = false;
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
if (resolved) {
if (autowireNecessary) {
return autowireConstructor(beanName, mbd, null, null);
}
else {
return instantiateBean(beanName, mbd);
}
}
// Candidate constructors for autowiring?
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
// Preferred constructors for default construction?
ctors = mbd.getPreferredConstructors();
if (ctors != null) {
return autowireConstructor(beanName, mbd, ctors, null);
}
// No special handling: simply use no-arg constructor.
return instantiateBean(beanName, mbd);
}
我们主要关注进行实例化的几个方法:
- 通过
BeanDefinition中的instanceSupplier直接获取一个实例化的对象。这个instanceSupplier属性我本身不是特别理解,在xml中的标签以及注解的方式都没有找到方式配置这个属性。后来在org.springframework.context.support.GenericApplicationContext这个类中找到了以下两个方法

经过断点测试,发现这种情况下,在实例化对象时会进入上面的supplier方法。下面是测试代码:
public static void main(String[] args) {
// AnnotationConfigApplicationContext是GenericApplicationContext的一个子类
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
ac.registerBean("service", Service.class,Service::new);
ac.refresh();
System.out.println(ac.getBean("service"));
}
可以发现进入了这个方法进行实例化

这个方法一般不常用,平常我们也使用不到,就不做过多探究,笔者认为,这应该是Spring提供的一种方便外部扩展的手段,让开发者能够更加灵活的实例化一个bean。
- 接下来我们通过不同的创建bean的手段,来分别验证对象的实例化方法
- 通过
@compent,@Service等注解的方式
测试代码:
public class Main {
public static void main(String[] args) {
// 通过配置类扫描
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
System.out.println(ac.getBean(Service.class));
}
}
@Component
public class Service {
}
观察debug:

可以发现,代码执行到最后一行,同时我们看代码上面的注释可以知道,当没有进行特殊的处理的时候,默认会使用无参构造函数进行对象的实例化
- 通过普通XML的方式(同
@compent注解,这里就不赘诉了) - 通过
@Configuration注解的方式
测试代码:
public class Main {
public static void main(String[] args) {
// 通过配置类扫描
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
// 这里将测试对象换为config即可,同时记得将条件断点更改为beanName.equlas("config")
System.out.println(ac.getBean(config.class));
}
}

同样,断点也进入最后一行
- 通过
@Bean的方式
测试代码:
@Configuration
@ComponentScan("com.dmz.official")
public class Config {
@Bean
public Service service(){
return new Service();
}
}
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac =
new AnnotationConfigApplicationContext(Config.class);
System.out.println(ac.getBean("service"));
}
}
断点结果:

可以发现,通过@Bean方法创建对象时,Spring底层是通过factoryMethod的方法进行实例化对象的。Spring会在我们需要实例化的这个对象对应的BeanDefinition中记录factoryBeanName是什么(在上面的例子中factoryBeanName就是config),同时会记录这个factoryBean中创建对象的factoryMethodName是什么,最后通过factoryBeanName获取一个Bean然后反射调用factoryMethod实例化一个对象。
这里我们需要注意几个概念:
- 这里所说的通过静态工厂方式通过
factoryBeanName获取一个Bean,注意,这个Bean,不是一个FactoryBean。也就是说不是一个实现了org.springframework.beans.factory.FactoryBean接口的Bean。至于什么是FactoryBean我们在后面的文章会认真分析 - 提到了一个概念
BeanDefinition,它就是Spring对自己所管理的Bean的一个抽象。不懂可以暂且跳过,后面有文章会讲到。
- 通过静态工厂方法的方式
测试代码:
public static void main(String[] args) {
ClassPathXmlApplicationContext cc =
new ClassPathXmlApplicationContext("application.xml");
System.out.println(cc.getBean("service"));
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- <bean id="myServiceImpl" class="com.dmz.official.service.Service"/>-->
<!-- the factory bean, which contains a method called get() -->
<bean id="myFactoryBean" class="com.dmz.official.service.MyFactoryBean">
<!-- inject any dependencies required by this locator bean -->
</bean>
<!-- 测试实例工厂方法创建对象-->
<bean id="clientService"
factory-bean="myFactoryBean"
factory-method="get"/>
<!--测试静态工厂方法创建对象-->
<bean id="service"
class="com.dmz.official.service.MyFactoryBean"
factory-method="staticGet"/>
</beans>
断点如下:

可以发现,这种情况也进入了instantiateUsingFactoryMethod方法中。通过静态工厂方法这种方式特殊之处在于,包含这个静态方法的类,不需要实例化,不需要被Spring管理。Spring的调用逻辑大概是:
- 通过
<bean>标签中的class属性得到一个Class对象 - 通过Class对象获取到对应的方法名称的Method对象
- 最后反射调用
Method.invoke(null,args)
因为是静态方法,方法在执行时,不需要一个对象。
- 通过实例工厂方法的方式
测试代码(配置文件不变):
public static void main(String[] args) {
ClassPathXmlApplicationContext cc =
new ClassPathXmlApplicationContext("application.xml");
System.out.println(cc.getBean("clientService"));
}
断点如下:

还是执行的这个方法。这个方法的执行过程我断点跟踪了以后,发现跟@Bean方式执行的流程是一样的。这里也不再赘述了。
到这里,这段代码我们算结合官网大致过了一遍。其实还遗留了以下几个问题:
- Spring是如何推断构造函数的?我们在上面验证的都是无参的构造函数,并且只提供了一个构造函数
- Spring是如何推断方法的?不管是静态工厂方法,还是实例工厂方法的方式,我们都只在类中提供了一个跟配置匹配的方法名,假设我们对方法进行了重载呢?
要说清楚这两个问题需要比较深入的研究代码,同时进行测试。我们在官网学习过程中,暂时不去强求这类问题。这里提出来是为了在源码学习过程中,我们可以带一定目的性去阅读。
实例化总结:
对象实例化,只是得到一个对象,还不是一个完全的Spring中的Bean,我们实例化后的这个对象还没有完成依赖注入,没有走完一系列的声明周期,这里需要大家注意
Spring官网上指明了,在Spring中实例化一个对象有三种方式:
- 构造函数
- 实例工厂方法
- 静态工厂方法
我自己总结如下结论:
Spring通过解析我们的配置元数据,以及我们提供的类对象得到一个Beanfinition对象。通过这个对象可以实例化出一个java bean对象。主要流程如图:

这篇文章到这里就结束了,主要学习了Spring官网中的1.2,1.3两小节。下篇文章,我们开始学习1.4中的知识。主要涉及到依赖注入的一些内容,也是我们Spring中非常重要的一块内容哦!下篇文章再见!
Spring官网阅读(一)容器及实例化的更多相关文章
- Spring官网阅读 | 总结篇
接近用了4个多月的时间,完成了整个<Spring官网阅读>系列的文章,本文主要对本系列所有的文章做一个总结,同时也将所有的目录汇总成一篇文章方便各位读者来阅读. 下面这张图是我整个的写作大 ...
- Spring官网阅读(十八)Spring中的AOP
文章目录 什么是AOP AOP中的核心概念 切面 连接点 通知 切点 引入 目标对象 代理对象 织入 Spring中如何使用AOP 1.开启AOP 2.申明切面 3.申明切点 切点表达式 excecu ...
- Spring官网阅读(十七)Spring中的数据校验
文章目录 Java中的数据校验 Bean Validation(JSR 380) 使用示例 Spring对Bean Validation的支持 Spring中的Validator 接口定义 UML类图 ...
- Spring官网阅读(十六)Spring中的数据绑定
文章目录 DataBinder UML类图 使用示例 源码分析 bind方法 doBind方法 applyPropertyValues方法 获取一个属性访问器 通过属性访问器直接set属性值 1.se ...
- Spring官网阅读(三)自动注入
上篇文章我们已经学习了1.4小结中关于依赖注入跟方法注入的内容.这篇文章我们继续学习这结中的其他内容,顺便解决下我们上篇文章留下来的一个问题-----注入模型. 文章目录 前言: 自动注入: 自动注入 ...
- Spring官网阅读(二)(依赖注入及方法注入)
上篇文章我们学习了官网中的1.2,1.3两小节,主要是涉及了容器,以及Spring实例化对象的一些知识.这篇文章我们继续学习Spring官网,主要是针对1.4小节,主要涉及到Spring的依赖注入.虽 ...
- Spring官网阅读(八)容器的扩展点(三)(BeanPostProcessor)
在前面两篇关于容器扩展点的文章中,我们已经完成了对BeanFactoryPostProcessor很FactoryBean的学习,对于BeanFactoryPostProcessor而言,它能让我们对 ...
- Spring官网阅读(六)容器的扩展点(一)BeanFactoryPostProcessor
之前的文章我们已经学习完了BeanDefinition的基本概念跟合并,其中多次提到了容器的扩展点,这篇文章我们就开始学习这方面的知识.这部分内容主要涉及官网中的1.8小结.按照官网介绍来说,容器的扩 ...
- Spring官网阅读(七)容器的扩展点(二)FactoryBean
在上篇文章中我们已经对容器的第一个扩展点(BeanFactoryPostProcessor)做了一系列的介绍.其中主要介绍了Spring容器中BeanFactoryPostProcessor的执行流程 ...
随机推荐
- DVWA渗透笔记
Command Injection Low <?php if( isset( $_POST[ 'Submit' ] ) ) { // Get input $target = $_REQUEST[ ...
- Java包机制和Javadoc的使用
1.什么是包机制? 包(package)其实本质上就是一个文件夹,使用包是为了让相同类名的两个类可以使用,也就是操作系统中的文件夹,用来解决重名并且让相同的功能类放在同一个包,使开发更加有条理. 注意 ...
- 005-循环结构(上)-C语言笔记
005-循环结构(上)-C语言笔记 学习目标 1.[掌握]switch-case结构 2.[理解]case语句穿透 3.[理解]Xcode断点调试 4.[理解]while循环结构初体验 5.[掌握]w ...
- Key Set HDU - 5363
这个题目套公式 2^(n-1)-1,再来个快速幂基本上就可以AC了 写这个题目的: 公式容易推到错: 容易写成 2^n-1/2...这样写出来结果也不错 但是一直哇 AC: #include< ...
- 详解 List接口
本篇博文所讲解的这两个类,都是泛型类(关于泛型,本人在之前的博文中提到过),我们在学习C语言时,对于数据的存储,用的差不多都是数组和链表. 但是,在Java中,链表就相对地失去了它的存在价值,因为Ja ...
- BUUOJ [CISCN2019 华北赛区 Day2 Web1]Hack World
补一下这道题,顺便发篇博客 不知道今年国赛是什么时候,菜鸡还是来刷刷题好了 0X01 考点 SQL注入.盲注.数字型 0X02自己尝试 尝试输入1 赵师傅需要女朋友吗???随便都能有好吧 输入2 ?? ...
- golang实现并发爬虫二(简单调度器)
上篇文章当中实现了单任务版爬虫. 那么这篇文章就大概说下,如何在上一个版本中进行升级改造,使之成为一个多任务版本的爬虫.加快我们爬取的速度. 话不多说,先看图: 其实呢,实现方法就是加了一个sched ...
- C#集合类型——Hashtable、Dictionary之浅谈
Hashtable表 数组.数组集合.List集合都是通过索引来访问里面成员.哈希表则是通过键来访问成员值.键不可为空,值可为空. 比如: Hashtable hash=new Hashtable ...
- NER命名实体识别,实体级level的评估,精确率、召回率和F1值
pre = "0 0 B_SONG I_SONG I_SONG 0 B_SONG I_SONG I_SONG 0 0 B_SINGER I_SINGER I_SINGER 0 O O O B ...
- 计算某天的下一天:黑盒测试之等价类划分+JUnit参数化测试
题目要求 测试以下程序:该程序有三个输入变量month.day.year(month.day和year均为整数值,并且满足:1≤month≤12.1≤day≤31和1900≤year≤2050),分别 ...