Spring 源码系列

1、Spring 学习之扩展点总结之后置处理器(一)
2、Spring 学习之扩展点总结之后置处理器(二)
3、Spring 学习之扩展点总结之自定义事件(三)
4、Spring 学习之扩展点总结之内置事件(四)
5、Spring 学习之扩展点总结之@Import(五)

文章目录

  • Spring 源码系列
  • 前言
  • 一、BeanDefinition介绍
  • 二、Bean实例化扩展点
    • 2.1、BeanFactoryPostProcessor
    • 2.2、BeanPostProcessor
    • 2.3、Aware
      • 2.3.1、ApplicationContextAware
      • 2.3.2、BeanFactoryAware
  • 三、总结

前言

本文用 Spring-framework-5.x 版本,Spring 源码用的是 Gradle 管理的,下载源码用 Gradle 进行编译即可在开发工具(idea)里面进行测试。

一、BeanDefinition介绍

我们平时使用 new 关键字来实例化一个 Class,得到的就是一个实例对象。
BeanDefinition从字面上理解就是 Bean 的定义,它是用来描述 Bean 的;Spring 就是一个 Bean 工厂,用来帮助我们来管理各种 Bean 的,Bean 也就是对象。
Spring里面管理着各种各样的对象,而且提供了很多扩展的地方供我们创建或者是修改 Bean 对象,那么在一个个 Bean 实例化之前,就是被定义为 BeanDefinition。
下面来看看 BeanDefinition 的类图:

从图中可以看出,BeanDefinition 继承了两个接口,AttributeAccessor 和 BeanMetadataElement。
AttributeAccessor:提供对 BeanDefinition 的操作能力;
BeanMetadataElement:BeanDefinition元数据,返回该Bean的来源;
BeanDefinition 里面存放着 Bean 的一系列信息,Bean 的作用域, 所对应的 Class, 是否懒加载,是否 Primary 等信息。

二、Bean实例化扩展点

上面大概了解一下什么是 BeanDefinition,Spring 就是通过它来实例化各种的 Bean 实例,也有很多可扩展的方法供我们在开发中修改或者自定义 Bean。

2.1、BeanFactoryPostProcessor

BeanFactoryPostProcessor 是一个 Bean 实例化随后就会执行的一个操作,我们可以实现此接口对 Bean 进行一个修改操作,来实现我们自己的逻辑,实现方式如下。
Spring 就是通过 ConfigurationClassPostProcessor 来注册 @Configuration 配置类的,ConfigurationClassPostProcessor 实现了 BeanDefinitionRegistryPostProcessor, 然而 BeanDefinitionRegistryPostProcessor 又继承了 BeanFactoryPostProcessor 后置处理器。

0、首先,需要创建一个服务类,并且把它交给 Spring 管理,Car 就是一个普通类,这里用 @Component 注解,让 Spring 扫描到它。

@Component
public class Car {public Car(int id, String name) {this.id = id;this.name = name;}private int id;private String name = "奥迪";private Plane plane;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Plane getPlane() {return plane;}public void setPlane(Plane plane) {this.plane = plane;}
}

1、 然后,需要创建一个类实现 BeanFactoryPostProcessor 接口并且复写 postProcessBeanFactory 方法,这个方法可以接受到 ConfigurableListableBeanFactory 的一个参数 beanFactory,通过 beanFactory 就可以获取到需要自定义操作的 Bean并且进行修改了;这里我们拿到 car 的 Bean 定义,并且设置 属性值。
注意:需要加上 @Component 注解让 Spring 扫描到自定义后置处理器并且可以调用。

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {GenericBeanDefinition carBean = (GenericBeanDefinition) beanFactory.getBeanDefinition("car");carBean.setScope(BeanDefinition.SCOPE_SINGLETON);ConstructorArgumentValues values = new ConstructorArgumentValues();//设置构造values.addIndexedArgumentValue(0, 1);values.addIndexedArgumentValue(1, "法拉利");carBean.setConstructorArgumentValues(values);//设置属性MutablePropertyValues propertyValues = new MutablePropertyValues();propertyValues.addPropertyValue("plane", new Plane());carBean.setPropertyValues(propertyValues);}
}

2、 main 调用测试

因为我们用的都是注解的形式让 Spring 扫描到所有的 Bean, 所以 main 方法中用 AnnotationConfigApplicationContext 来启动 Spring。

@ComponentScan("com.self.test")
public class MainStartApp {public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainStartApp.class);Car car = (Car) applicationContext.getBean("car");System.out.println(car.getName());System.out.println("车里面的飞机:" + car.getPlane().getName());}}

调用结果中可以看出来,我在main方法中,获取到的 Car 实例,打印 car.name 的值,在 Car 类里面,默认 name 是 “奥迪”,但是通过后置处理器修改值后,变成了 “法拉利”,Plan 对象也有值了。

3、 调用流程跟踪分析

2.2、BeanPostProcessor

BeanPostProcessor 也是Bean的一个后置处理器,这个后置处理器里面有两个方法:
1:postProcessBeforeInitialization(Object bean, String beanName)
此方法是在初始化一个 Bean 之前调用。
2:postProcessAfterInitialization(Object bean, String beanName)
此方法是在初始化一个 Bean 之后调用。
所以,BeanPostProcessor 在每一个 Bean 的实例化时会被调用两次。

0、 首先创建一个类 Car, 同 2.1 的第一步。
1、 然后创建一个自定义后置处理器类 ValueBeanPostProcessor 继承类 BeanPostProcessor,并且复写 postProcessBeforeInitialization 和 postProcessAfterInitialization 方法,也是需要 @Component 注解的。

@Component
public class ValueBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if(bean.getClass() == Car.class){Car car = (Car) bean;car.setName("奥迪A6");Plane plane = new Plane();plane.setName("玩具飞机");car.setPlane(plane);return car;}return null;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if(bean.getClass() == Car.class){Car car = (Car) bean;car.getPlane().setName("轰炸鸡");}return null;}
}

2、main 调用测试

@ComponentScan("com.self.test")
public class MainStartApp {public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainStartApp.class);Car car = (Car) applicationContext.getBean("car");System.out.println(car.getName());System.out.println("车里面的飞机:" + car.getPlane().getName());}}

测试结果:

从测试结果中可以看出,在 BeforeInitialization 中设置的是 plan.name 是 “玩具飞机”,但是通过 AfterInitialization 方法改为 “轰炸鸡” 之后,最终 car.plan.name 是 轰炸鸡。

3、调用流程分析跟踪

2.3、Aware

Aware 接口是一个 Spring 容器的核心接口,具有标识作用的超级接口;
也是一个空接口,功能都有子类决定,该接口具有 Spring 容器通知的能力,采用回调的方式进行通知。
Aware 的子类通常有一个接受单参数的 setXXX() 方法。

2.3.1、ApplicationContextAware

ApplicationContextAware 继承自 Aware 接口,提供了一个 setApplicationContext(ApplicationContext applicationContext) 方法,提供了上下文,可以拿到所有Bean进行相应的操作。

1、创建一个 UserServiceImpl 类,实现 UserService 接口,然后用 @Service 注解标注。

@Service
public class UserServiceImpl implements UserService {private String name;@Overridepublic void sayHi() {System.out.println(String.format("被注入的 name: %s", name));}public String getName() {return name;}public void setName(String name) {this.name = name;}
}

2、创建一个类实现 ApplicationContextAware 接口并且复写 setApplicationContext 方法。

@Component
public class UpdateBeanApplicationContextAware implements ApplicationContextAware {@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {UserServiceImpl bean = applicationContext.getBean(UserServiceImpl.class);bean.setName("Yphen");}
}

3、main 测试类

@ComponentScan("com.self.test")
public class MainStartApp {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainStartApp.class);UserServiceImpl bean = applicationContext.getBean(UserServiceImpl.class);bean.sayHi();}}

测试结果:

从测试结果可以看出,打印出来的 “Yphen” 就是通过上下文设置进去的。

2.3.2、BeanFactoryAware

BeanFactoryAware 也是集成了 Aware,和 ApplicationContextAware 类似,具有Spring 通知能力,也是通过 setBeanFactory 来修改 Bean 实例。

1、新建一个 UserServiceImpl 类,同 2.3.2.1。
2、新建一个 ChangeBeanFactoryAware 类实现 BeanFactoryAware 接口,并且复写 setBeanFactory(BeanFactory beanFactory) 方法。

@Component
public class ChangeBeanFactoryAware implements BeanFactoryAware {@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {UserServiceImpl bean = beanFactory.getBean(UserServiceImpl.class);bean.setName("BeanFactoryAware 设置值为 Yphen");}
}

3、main 测试类

@ComponentScan("com.self.test")
public class MainStartApp {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainStartApp.class);UserServiceImpl bean = applicationContext.getBean(UserServiceImpl.class);bean.sayHi();}
}

测试结果:

从测试结果可以看出,成功通过 BeanFactoryAware 从上下文中设置了 UserServiceImpl 的 name 的值。

三、总结

Spring 就是一个 Bean 工厂,那么在 Bean 创建的过程中是经过了好几个步骤的,不是简单的 new 就行,因为我们在项目中用到的 Bean 里面又有 Bean, 就像一个 Controller 的 Bean 里面 有 Service 的 Bean, 一个 Service 的 Bean 里面 还有 其他的实例,所以Spring初始化完成的一个实例是比较复杂的,而且还暴露了很多扩展点,让我们通过这些扩展点去修改和创建自定义的,特殊场景需要的实例,本片文章简单的讲了 BeanFactoryPostProcessor、BeanPostProcessor、Aware 等扩展点,Event 监听等其他的扩展点可移步其他篇章。

Spring 学习之扩展点总结之后置处理器(一)相关推荐

  1. spring源码:九大后置处理器

    目的: spring在完成一个bean的初始化.实例化的过程中,会用到九个后置处理器:本文梳理出这九个后置处理器 九大后置处理器 spring在初始化的过程中,会在九个地方分别调用了五个后置处理的九个 ...

  2. 通过Spring的BeanPostProcessor的 bean的后置处理器会拦截所有bean创建过程

    postProcessBeforeInitialization 在init方法之前调用 postProcessAfterInitialization 在init方法之后调用 package com.C ...

  3. Spring学习理解---后置处理器

    Spring学习理解之-后置处理器 spring后置处理器有几种?后置处理器的作用 后置处理器有两种: (1)bean的后置处理器:这种处理器会对容器中的bean进行后处理,对bean进行增强 (2) ...

  4. Spring学习笔记八--Bean生命周期和后置处理器

    为什么80%的码农都做不了架构师?>>>    Bean生命周期和后置处理器 IOC容器的bean生命周期 1.构造器或工厂方法建立bean实例 2.bean属性赋值,引用其他bea ...

  5. spring学习12 -Spring 框架模块以及面试常见问题注解等

    以下为spring常见面试问题: 1.Spring 框架中都用到了哪些设计模式? Spring框架中使用到了大量的设计模式,下面列举了比较有代表性的: 代理模式-在AOP和remoting中被用的比较 ...

  6. spring的后置处理器(未完结版)

    学习spring源码也有一阶段时间了,是时候收货的季节了,打算写几篇博客,把自己的知识沉淀下来.在学习spring的源码之前,别人问我spring什么牛逼.我会毫不犹豫的说出AOP,IOC啊.但是看看 ...

  7. Spring——Spring学习教程(详细)(上篇)——IOC、AOP

    本文是Spring的学习上篇,主要讲IOC和AOP. Spring的JDBCTemplete以及事务的知识,请见下篇. Spring--Spring学习教程(详细)(下篇)--JDBCTemplete ...

  8. JavaEE——Spring学习笔记03【AOP开发】

    JavaEE--Spring学习笔记01[Ioc开发的模式] JavaEE--Spring学习笔记02[Spring和Mybatis的整合] JavaEE--Spring学习笔记03[AOP开发] J ...

  9. Spring核心技术(七)——Spring容器的扩展

    本文将讨论如何关于在Spring生命周期中扩展Spring中的Bean功能. 容器的扩展 通常来说,开发者不需要通过继承ApplicationContext来实现自己的子类扩展功能.但是Spring ...

最新文章

  1. 某微信公众号2019 SAP模块精华帖汇总
  2. cassandra的全文检索插件
  3. Java 多线程常见问题
  4. Dockerfile项目环境介绍
  5. 零基础学Python:自定义序列类的详细教程
  6. 表格大小设置_系统地学习Excel第18课,设置单元格字体格式
  7. python发送电子邮件
  8. Adapter(适配器)模式
  9. 头歌平台-人工智能之AlphaBeta剪枝算法
  10. android 删除系统服务,不ROOT卸载系统自带应用
  11. 【Vivado那些事儿】Vivado介绍
  12. BZOJ2286: [Sdoi2011]消耗战 虚树
  13. 计算机D盘无法读取,D盘目录或文件被损坏且无法读取的愿意以及解决办法
  14. canvas实现旋转缩放的方块
  15. 小程序canvas输出gif格式的图片作为表情
  16. Aircrack-ng 工具箱
  17. ubuntu 修改默认用户名_Ubuntu默认的用户名和密码是什么?
  18. easy_nbt——Bugku
  19. 便来亭心里坐下 水浒
  20. Python爬取电商平台充气娃娃用户评价,看看用户体验是什么样的!

热门文章

  1. AndroidStudio 弹出的Safe Delete 安全删除功能是什么
  2. 广东老火靓汤108种
  3. linux32系统下载地址,deepin 15.3 32位ISO下载地址,深度系统最后支持32位的版本
  4. 灵狐剪辑,ai混剪,tiktok批量去重工具功能介绍
  5. ios 渐变透明背景_渐变色彩的室内应用技巧
  6. Android 剩余可用时长的计算公式
  7. 长链剖分(知识点整理+板子总结)
  8. 【MaixPY 教程】用mixly玩转k210——条形码、二维码、AprilTag识别
  9. 树莓派魔镜MagicMirror —— 3 HDMI连接显示器
  10. Yield Guild Games 和 MOBLAND 达成合作