Spring官网阅读(一)容器及实例化
从今天开始,我们一起过一遍Spring的官网,为Spring源码的学习打好基础。在这个过程中,不会涉及过多底层的代码,更多是通过例子证明我们在官网得出的结论,希望自己可以坚持下来,给自己加个油!!!
本文主要涉及到官网中的
1.2
,1.3
节。
Spring容器
容器是什么?
我们先看官网中的一句话:
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 框架的核心,是用来管理对象的。容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁。
容器如何工作?
我们直接看官网上的一张图片,如下:
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实例化这个beanSupplier<?> instanceSupplier = mbd.getInstanceSupplier();if (instanceSupplier != null) {return obtainFromSupplier(instanceSupplier, beanName);}// 3.通过FactoryMethod实例化这个beanif (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 {@Beanpublic 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官网阅读(三)自动注入
上篇文章我们已经学习了1.4小结中关于依赖注入跟方法注入的内容.这篇文章我们继续学习这结中的其他内容,顺便解决下我们上篇文章留下来的一个问题-----注入模型. 前言: 在看下面的内容之前,我们先要对 ...
- Spring官网阅读(二)(依赖注入及方法注入)
上篇文章我们学习了官网中的1.2,1.3两小节,主要是涉及了容器,以及Spring实例化对象的一些知识.这篇文章我们继续学习Spring官网,主要是针对1.4小节,主要涉及到Spring的依赖注入.虽 ...
- Spring 官网阅读指南
一.概述 Spring官网:https://spring.io/,界面如下: 二.各模块介绍 进入首页如上图,在首页官网会展示一些当前Spring比较流行的技术.可以看见导航栏有几个模块,信息如下: ...
- Spring官网阅读(四)BeanDefinition(上)
前面几篇文章已经学习了官网中的1.2,1.3,1.4三小结,主要是容器,Bean的实例化及Bean之间的依赖关系等.这篇文章,我们继续官网的学习,主要是BeanDefinition的相关知识,这是Sp ...
- Spring官网阅读(九)Spring中Bean的生命周期(上)
文章目录 生命周期回调 1.Bean初始化回调 2.Bean销毁回调 3.配置默认的初始化及销毁方法 4.执行顺序 5.容器启动或停止回调 Lifecycle 接口 LifecycleProcesso ...
- Spring官网和源码系列-如何阅读
作为一名Java从业者,有听过这么一句话,Spring源码至少看三遍才能算是一名软件工程师.对于读源码,大家就觉得很头疼,潜意识里感觉枯燥.难,基本就是能不读就不读,工作中够用就行了.不读源码的确可以 ...
- Spring官网学习(一)概述
文章目录 1.Spring官网简介 2.Spring总览 2.1.什么是Spring 2.2.Spring的发展历程 3.Spring的设计理念 3.1.Spring的优点 4.IOC和AOP浅析 4 ...
- Spring官网改版后下载方式
2019独角兽企业重金招聘Python工程师标准>>> Spring官网改版后,很多项目的完整zip包下载链接已经隐掉了,虽然Spring旨在引导大家用更"高大上" ...
最新文章
- 集合、泛型、增强for
- 新冠肺炎数据里学到的四个数据分析和机器学习知识
- node搭建的一个应用在前端项目中的可切换接口的代理服务器
- Day1 - Python基础1作业【编写登陆接口】
- SolidWorks学习笔记4特征
- mysql 建表时建立索引_mysql 分享建表和索引的几点规范
- 7个使用JavaScript构建数据可视化的实用工具库,希望你能喜欢
- 深入理解js构造函数
- boost准模板库内存管理中pool和object_pool的使用
- 关于Python切片的问题,从左向右提取元素与从右向左提取元素
- 下载安装python函数库
- 电池充电器UL1310、启动电源UL2743、电脑风扇507测试报告怎么办理?
- H5制作哪家强?四大H5页面制作工具大比拼
- linux常见的实用的问题,linux常见问题及答案
- 鹏业安装算量复制工程量
- 用latex排版中的对象旋转问题
- 计算机保密dss是啥,什么是DSS?
- 简单使用SAXReader解析xml数据
- 寻找解决样本不均衡方法之Focal Loss与GHM
- Java SE 高级教程
热门文章
- 今天碰到一道比较有趣的面试题,大家来探讨一下。
- Scala基础教程(八):模式匹配、正则表达式
- python 字符串格式化,使用f前缀
- 使用nodejs搭建HTTPS server
- html5--4-3 source元素-解决浏览器的兼容
- JAVA常见算法题(三十二)---找规律
- 初探面向对象编程之oop与设计模式
- 问题 “cell 出栈 selectBox 已选的图标,被释放掉,再次进入屏幕时,没有了已选图标 ” 解决方案...
- 【Hibernate框架开发之五】Hibernate对象的三种状态Session常用方法
- OBS集成WebRTC