Spring 的核心组件详解
Spring 总共有十几个组件,但是真正核心的组件只有三个:Core、Context 和 Bean。它们构建起了整个 Spring的骨骼架构,没有它们就不可能有 AOP、Web 等上层的特性功能。
一、Spring的设计理念
Bean组件是 Spring核心中的重点,Spring 就是面向Bean编程的(Bean Oriented Programming:BOP)就像Object 对OOP的意义一样,没有对象的概念就像没有面向对象的编程,在Spring中没有Bean也就没有Spring存在的意义。我们使用 Spring的主要一个原因就是 Spring会把对象之间的依赖关系转而用配置文件来管理。也就是依赖注入机制。而这个注入关系在一个叫 IOC的容器中管理,而IOC容器就是被Bean包裹的对象。Spring正是通过把对象包装在Bean中从而达到管理这些对象及做一系列额外操作的目的。
前面如果把 Bean比作一场演出中的演员,Context就是这场演出的舞台背景,而 Core应该就是演出的道具了。Bean包装的是Object,而 Object必然有数据,如何给这些数据提供生存环境就是Context要解决的问题,对Context来说它就是要发现每个Bean之间的关系并维护好这种关系。所以Context就是一个Bean关系的集合,这种关系集合又叫 IOC容器。Core组件用来发现、建立和维护每个 Bean之间所需的一系列工具,把 Core组件也可以看成是Util。
二、Bean组件
Bean组件在Spring的 org.springframework.beans 包下,在这个包下的所有类主要解决了3件事:Bean的定义、Bean的创建及对Bean的解析。对Spring使用者来说唯一需要关心的就是Bean的创建,其他两个由Spring在内部完成。BeanDefination:Bean的定义完整的描述了在 Spring配置文件中定义的<bean/>节点中所有的信息,包括各种子节点。当 Spring成功解析 <bean/> 节点后,在Spring内部它就被转化成 BeanDefinition对象,以后所有操作都是对这个对象操作。
Bean的解析过程非常复杂,功能被分的很细。因为这里需要被扩展的地方很多,必须保证有足够的灵活性,以应对可能的变化。Bean的解析主要就是对Spring配置文件的解析。
三、Context组件
Context 在 Spring的 org.springframework.context 包下,给 Spring提供一个运行时的环境,用于保存各个对象的状态。ApplicationContext 是 Context 的父类,它除了能标识一个应用环境的基本信息外,还集成了5个接口来扩展 Context 的功能。例如:通过继承 BeanFactory 表明容器中运行的主体对象是 Bean,另外继承了 ResourceLoader接口,使得 ApplicationContext可以访问外部资源(在Core中说明);
【1】ApplicationContext 的子类主要包含两个方面:
● ConfigurableApplicationContext 表示该 Context 是可修改的,也就是在构建 Context 中,用户可以动态添加或修改已有的配置信息,其中最经常使用的是可更新的 Context,即 AbstractRefreshableApplicationContex类;
● WebApplicationContext 为Web装备的Context,可直接访问ServletContext;
【2】再往下分别就是构建 Context的文件类型,接着就是访问 Context的方式,这样一级一级构成了完成的 Context等级层次。总体来说 ApplicationContext 必须要完成一下几件事情:
标识一个应用环境;
利用 BeanFactory创建Bean对象;
保存对象关系表;
能够捕捉各种事件;
Context 作为 Spring的IOC容器,基本上整合了 Spring的大部分功能,或者说是大部分功能的基础。
四、Core组件
Core 组件作为 Spring的核心组件,其中包含了很多关键类,例如:定义了资源的访问方式。这种将所有资源都抽象成一个接口的方式很值得以后的设计中拿来学习。
Context 与 Core之间的关系:比如 Context一般会把资源的加载、解析和描述工作委托给 ResourcePatternResolver类来完成,它相当于一个接头人,把资源的加载、解析和资源的定义整合在一起便于其他组件使用,在Core组件中还有很多类似的方式。
五、IOC容器如何工作
IOC容器实际上是 Context组件结合其他两个组件共同构建了一个 Bean关系网,如何构建这个关系网,构建的入口就在AbstractApplicationContext 类的 refresh方法中,这个方法代码如下:https://www.cnblogs.com/GooPolaris/p/8184429.html
1 @Override
2 public void refresh() throws BeansException, IllegalStateException {
3 synchronized (this.startupShutdownMonitor) {
4 // 为刷新做准备的 Context
5 prepareRefresh();
6 // 刷新所有BeanFactory子容器
7 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
8 // 创建BeanFactory
9 prepareBeanFactory(beanFactory);
10
11 try {
12 // 注册实现了BeanPostProcess接口的Bean
13 postProcessBeanFactory(beanFactory);
14 // 初始化和执行BeanFactoryPostProcess beans
15 invokeBeanFactoryPostProcessors(beanFactory);
16 // 初始化和执行BeanPostProcess beans
17 registerBeanPostProcessors(beanFactory);
18 // 初始化 message source
19 initMessageSource();
20 // 初始化 event multicaster
21 initApplicationEventMulticaster();
22 // 刷新子类实现的方法
23 onRefresh();
24 // 检查注册事件
25 registerListeners();
26 // 初始化 (non-lazy-init) 单例Bean
27 finishBeanFactoryInitialization(beanFactory);
28 // 执行LifecycleProcessor.onRefresh()和ContextRefreshedEvent事件
29 finishRefresh();
30 }
31
32 catch (BeansException ex) {
33 destroyBeans();//如果刷新失败那么就会将已经创建好的单例Bean销毁掉
34 cancelRefresh(ex);//重置context的活动状态
35 throw ex;//抛出异常
36 }
37 }
38 }
这个方法就是构建整个 IOC容器过程的完整代码,了解里面的每一行代码,基本上就了解了大部分 Spring的原理和功能。这段主要包含一下步骤:
1)、构建BeanFactory,以便于产生所需的“演员”;
2)、注册可能感兴趣的事件;
3)、创建Bean实例对象;
4)、触发被监听的事件;
首先创建和配置BeanFactory《容器》,这里是refresh,也就是刷新配置。前面介绍了Context有可更新的子类,这里正是实现这个功能,当BeanFactory存在时就更新,如果不存在就新创建。更新BeanFactory的方法如下:
1 @Override
2 protected final void refreshBeanFactory() throws BeansException {
3 if (hasBeanFactory()) {//如果已经存在一个bean工厂那么就将其销毁,关闭。
4 destroyBeans();
5 closeBeanFactory();
6 }
7 try {
8 DefaultListableBeanFactory beanFactory = createBeanFactory();//新建一个Bean工厂。
9 beanFactory.setSerializationId(getId());
10 customizeBeanFactory(beanFactory);//自定义bean工厂。
11 loadBeanDefinitions(beanFactory);//加载BeanDefinition
12 synchronized (this.beanFactoryMonitor) {
13 this.beanFactory = beanFactory;//将创建好的bean工厂的引用交给的context来管理。
14 }
15 }
16 catch (IOException ex) {//加载bean定义资源的时候可能会抛出异常。
17 throw new ApplicationContextException("I/O error parsing bean definition source for "
18 + getDisplayName(), ex);
19 }
20 }
这个方法实现了 AbstractApplictionContext 的抽象方法 refreshBeanFactory,这段代码清楚的说明了 BeanFactory的创建过程。注意 BeanFactory对象的类型变化,BeanFactory有很多子类,在什么情况下使用非常关键。BeanFactory的原始对象是DefaultListableBeanFactory,这个非常关键,因为它涉及后面对这个对象的多种操作,下面看一下这个类的继承关系图:
从上图可以发现除了 BeanFactory相关类外,还发现与 Bean 的 register 相关的类。这在 refreshBeanFactory方法的loadBeanDefinitions(beanFactory) 一行将找到答案,这个方法将加载、解析Bean的定义,也就是把用户定义的数据结构转化为IOC容器中的特定数据结构。
创建好 BeanFactory后,添加一些 Spring本身需要的工具类,这个操作在 AbstractApplictionContext 的 prepareBeanFactory的prepareBeanFactory方法中完成。
在 AbstractApplicationContext中接下来的3行代码 Spring的功能扩展性能起了至关重要的作用。前两行主要是让你现在可以对已经构建的 BeanFactory的配置做修改,后面一行就是让你可以对以后再创建 Bean的实例对象时添加一些自定义的操作。所以它们都扩展了 Spring的功能,要学习 Spring必须搞清楚这一部分。
其中在 invokeBeanFactoryPostProcessors方法中主要是获取实现 BeanFactoryPostProcess接口的子类,并执行它的postProcessBeanFactory方法,这个方法的声明如下:
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
它的参数是 beanFactory,说明可以对 beanFactory做修改,beanFactory是 ConfigurableListableBeanFactory类型的,这也印证了前面介绍的不同BeanFactory所使用的场合不同,这里只能是可配置的 BeanFactory,防止一些数据被用户随意修改。
registerBeanPostProcess方法也可以获取用户定义的实现了BeanPostProcessor接口的子类,并把他们注册到 BeanFacotry对象中的 beanPostProcessor变量中。在 BeanPostProcessor中声明了两个方法:postProcessBeforeInitialization和postProcessAfterInitialization,分别用于 Bean对象初始化时执行,可以执行用户自定义的操作。后面几行代码是初始化监听事件和对系统的其他监听者的注册,监听者必须是 ApplicationListener的子类。
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
//不使用TempClassLoader
beanFactory.setTempClassLoader(null);
//禁止修改当前Bean的配置信息
beanFactory.freezeConfiguration();
//实例化non-lazy-init类型的bean
beanFactory.preInstantiateSingletons();
}
从上面的代码中可以发现 Bean的实例化是在 BeanFactory中发生的。PreInstantiateSingletons方法的代码如下:
1 @Override
2 public void preInstantiateSingletons() throws BeansException {
3 if (this.logger.isDebugEnabled()) { //日志(这是一种好的日志处理方式,当没有开启debug级别时,就会省下一次字符串拼接),spring源码里到处都是这样的处理,可以学习。
4 this.logger.debug("Pre-instantiating singletons in " + this);
5 }
6
7 // Iterate over a copy to allow for init methods which in turn register new bean definitions.
8 // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
9 //拷贝一个新的list ,this.beanDefinitionNames 干嘛的,上文大概已有说明,可以结合《Spring 源码阅读 BeanFactory(二) 之registerBeanDefinition方法》一块看
10 List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
11
12 // Trigger initialization of all non-lazy singleton beans...
13 for (String beanName : beanNames) { //遍历beanName
14 //这里先解释一下getMergedLocalBeanDefinition方法的含义,因为这个方法会常常看到。Bean定义公共的抽象类是AbstractBeanDefinition,普通的Bean在Spring加载Bean定义的时候,实例化出来的是GenericBeanDefinition,而Spring上下文包括实例化所有Bean用的AbstractBeanDefinition是RootBeanDefinition,这时候就使用getMergedLocalBeanDefinition方法做了一次转化,将非RootBeanDefinition转换为RootBeanDefinition以供后续操作。
15 RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
16 if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {//非抽象的,单例的,非懒加载的
17 if (isFactoryBean(beanName)) { //Spring中有两种类型的Bean,一种是普通Bean,另一种是工厂Bean,即FactoryBean, 工厂Bean 再起炉灶
18 final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
19 boolean isEagerInit;
20 if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
21 isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
22 @Override
23 public Boolean run() {
24 return ((SmartFactoryBean<?>) factory).isEagerInit();
25 }
26 }, getAccessControlContext());
27 }
28 else {
29 isEagerInit = (factory instanceof SmartFactoryBean &&
30 ((SmartFactoryBean<?>) factory).isEagerInit());
31 }
32 if (isEagerInit) {
33 getBean(beanName);
34 }
35 }
36 else { //非工厂Bean 调用该方法
37 getBean(beanName);
38 }
39 }
40 }
41 }
这里出现了一个 Bean(Factory Bean),可以说 Spring有一大半的扩展功能与这个 Bean有关,它是一个工厂Bean,用于产生Bean实例,如果一个类继承 FactoryBean,用户可以自定义产生对象的方法,只需要实现它的 getObject方法即可。然而在Spring内部,这个Bean的实例对象是 FactoryBean通过调用这个对象的 getObject方法就能获取用户自定义产生的对象,从而为Spring 提供了很好的扩展性。Spring 获取 FactoryBean本身的对象是通过在前面加上&来完成的。
如何创建 Bean的实例对象及如何构建Bean实例对象之间的关联关系是 Spring中的一个核心,还有就是建立 Bean对象实例之间的关系,也是 Spring框架的核心竞争力。 Bean实例创建流程图如下:
六、IOC容器的扩展点
对 Spring的 IOC容器来说,主要有 BeanFactoryPostProcessor和 BeanPostProcessor,他们分别在构建 BeanFactory和构建Bean对象时调用。还有就是 InItializingBean 和 DisposableBean,他们分别在Bean创建和销毁时调用。用户可以实现在这些接口中定义的方法,Spring 会在适当的时候调用它们。还有一个是FactoryBean,它是个特殊的Bean,可以被用户更多的控制。
这些扩展点通常也是我们使用 Spring来完成特殊任务的地方,如何精通Spring就看是否掌握好Spring有哪些扩展点,以及如何使用它们。要知道如何使用它们就必须了解它们内在的机制。
ApplicationContext.xml 就是 IOC容器的默认配置文件,Spring 的所有特性功能都是基于 IOC容器工作的。我们可以通过实现Spring 的扩展点来改变 Spring的通用行为。至于如何实现扩展点来得到我们想要的结果,在Spring中有很多例子,其中AOP的实现就是通过扩展点达到想要的特性功能,可以拿来参考。
Spring 的核心组件详解的更多相关文章
- (转)Spring JdbcTemplate 方法详解
Spring JdbcTemplate方法详解 文章来源:http://blog.csdn.net/dyllove98/article/details/7772463 JdbcTemplate主要提供 ...
- Spring jar包详解
Spring jar包详解 org.springframework.aop ——Spring的面向切面编程,提供AOP(面向切面编程)的实现 org.springframework.asm——spri ...
- Spring——jar包详解(转)
Spring——jar包详解 org.springframework.aop ——Spring的面向切面编程,提供AOP(面向切面编程)的实现 org.springframework.asm——spr ...
- Spring Boot异常处理详解
在Spring MVC异常处理详解中,介绍了Spring MVC的异常处理体系,本文将讲解在此基础上Spring Boot为我们做了哪些工作.下图列出了Spring Boot中跟MVC异常处理相关的类 ...
- spring事务配置详解
一.前言 好几天没有在对spring进行学习了,由于这几天在赶项目,没有什么时间闲下来继续学习,导致spring核心架构详解没有继续下去,在接下来的时间里面,会继续对spring的核心架构在继续进行学 ...
- spring注入参数详解
spring注入参数详解 在Spring配置文件中, 用户不但可以将String, int等字面值注入到Bean中, 还可以将集合, Map等类型的数据注入到Bean中, 此外还可以注入配置文件中定义 ...
- Spring的lazy-init详解
1.Spring中lazy-init详解ApplicationContext实现的默认行为就是在启动服务器时将所有singleton bean提前进行实例化(也就是依赖注入).提前实例化意味着作为初始 ...
- Spring Security Filter详解
Spring Security Filter详解 汇总 Filter 作用 DelegatingFilterProxy Spring Security基于这个Filter建立拦截机制 Abstract ...
- Spring Boot 配置文件详解
Spring Boot配置文件详解 Spring Boot提供了两种常用的配置文件,分别是properties文件和yml文件.他们的作用都是修改Spring Boot自动配置的默认值.相对于prop ...
- spring原理案例-基本项目搭建 02 spring jar包详解 spring jar包的用途
Spring4 Jar包详解 SpringJava Spring AOP: Spring的面向切面编程,提供AOP(面向切面编程)的实现 Spring Aspects: Spring提供的对Aspec ...
随机推荐
- 安装S32 Design Studio及简单操作
安装S32 Design Studio及简单操作 说明:安装所需要的安装包均在此文件中,安装流程及基本操作按照入门手册PDF进行即可,以下着重记录所学基本操作流程,安装流程省略 安装: 安装流程 按照 ...
- 力扣:面试题58 - II. 左旋转字符串
菜鸡小白不禁感叹:C++真好用!!! 题目描述: 字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部.请定义一个函数实现字符串左旋转操作的功能.比如,输入字符串"abcdefg& ...
- 前端使用JSEncrypt进行加密和解密
下载链接:https://www.bootcdn.cn/jsencrypt/ 使用方法: 1.引入jsencrypt 2.使用方法 // 加密公钥 const key = `xxxxxx`; func ...
- protobuf协议 待整理
https://blog.51cto.com/wangjichuan/5691192 https://blog.csdn.net/lizhichao410/article/details/126032 ...
- mysql 设置相关
告诉mysql客户端这边的文字编码 告诉mysql希望返回的结果集编码: set character_set_client=gbk; set character_set_results=gbk; ...
- NOIP2015普及组
T1 金币 很简单的题,控制天数这个变量 #include<iostream> #include<cstring> #include<cmath> #include ...
- Lua元表应用举例:配置表格转为Lua配置表
把配置表格.xlsx数据转为Lua配置表,其实就是把表格数据用Lua写一遍,这里的实现重点就是setmetatable设置元表. 以下以表格student_info.xlsx举例,展示对应Lua配置表 ...
- 源代码管理工具介绍(以GITHUB为例)
Github:全球最大的社交编程及代码托管网站,可以托管各种git库,并提供一个web界面 1.基本概念 仓库(Repository):用来存放项目代码,每个项目对应一个仓库,多个开源项目则有多个仓库 ...
- 简单的继承练习——疑问:私有属性在类的方法内调用时,有必要使用get,set方法嘛?
要求: 创建一个Circle类,设置半径属性和返回面积的方法,在无参构造器中初始化半径值为1. 创建一个Cylinder类,继承Circle类,设置属性高和返回体积的方法,在无参构造器中初始化长度为1 ...
- Python操作数据库读书笔记
SQLite 简介 什么是 SQLite? SQLite是一个进程内的库,实现了自给自足的.无服务器的.零配置的.事务性的 SQL 数据库引擎.它是一个零配置的数据库,这意味着与其他数据库一样,您不需 ...