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

15 篇文章 2 订阅
7 篇文章 0 订阅

Spring 源码系列

1、Spring 学习之扩展点总结之后置处理器(一)
2、Spring 学习之扩展点总结之后置处理器(二)
3、Spring 学习之扩展点总结之自定义事件(三)
4、Spring 学习之扩展点总结之内置事件(四)
5、Spring 学习之扩展点总结之@Import(五)
6、Spring 学习之AOP总结之基础介绍(六)
7、Spring 学习之AOP总结之实现代理(七)
8、SpringBoot 学习之自动配置基本原理(八)
9、SpringBoot 学习之启动原理(九)
10、ElasticSearch学习随笔之SpringBoot Starter 操作
11、图数据库 Neo4j 学习之SpringBoot整合
12、SpringBoot 学习之常用 Filter / Advice 总结
13、SpringBoot+FastJson 优雅的过滤 Response Body
14、Spring Security + Oauth2 认证授权

前言

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

一、BeanDefinition介绍

我们平时使用 new 关键字来实例化一个 Class,得到的就是一个实例对象。
BeanDefinition从字面上理解就是 Bean 的定义,它是用来描述 Bean 的;Spring 就是一个 Bean 工厂,用来帮助我们来管理各种 Bean 的,Bean 也就是对象。
Spring里面管理着各种各样的对象,而且提供了很多扩展的地方供我们创建或者是修改 Bean 对象,那么在一个个 Bean 实例化之前,就是被定义为 BeanDefinition。
下面来看看 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 实例化之前对 BeanDefinition 执行的一系列操作,我们可以实现此接口对 BeanDefinition 进行一个修改操作,随后再进行 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 的 BeanDefinition 并且进行修改了;这里我们拿到 car 的 BeanDefinition 【Bean定义】,并且设置 属性值。
注意:需要加上 @Component 注解让 Spring 扫描到自定义后置处理器并且可以调用。

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

	@Override
	public 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、 调用流程跟踪分析
BeanDefinitionPostProcessor调用流程

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 {

	@Override
	public 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;
	}

	@Override
	public 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;

	@Override
	public 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 {

	@Override
	public 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 {

	@Override
	public 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 监听等其他的扩展点可移步其他篇章。

  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值