Spring2

参考

B站颜群老师Spring教程,B站雷丰阳老师Spring教程,《Spring实战》
https://zhuanlan.zhihu.com/p/137507309
https://liayun.blog.csdn.net/article/details/115053350

容器

快速导入一个组件

Spring 3.0之前,创建bean 可以通过XML 配置文件与扫描特定包下面的类
来将类注入到Spring IOC容器内。而在Spring 3.0之后提供了JavaConfig
的方式,也就是将IOC容器里面bean的元信息以Java代码的方式进行描述,
然后我们可以通过@Configuration与@Bean这两个注解配合使用来将原来
配置在XML文件里面的bean通过Java代码的方式进行描述

  1. 包扫描+给组件标注注解(@Controller、@Servcie、@Repository、
    @Component),但这种方式比较有局限性,局限于我们自己写的类
  2. @Bean注解,通常用于导入第三方包中的组件
  3. @Import注解,快速向Spring容器中导入一个组件

@Import注解概述

@Import注解提供了@Bean 注解的功能,同时还有XML配置文件里面标签组织
多个分散的XML文件的功能,当然在这里是组织多个分散的@Configuration
,因为一个配置类就约等于一个XML配置文件

1
2
3
4
5
//@Import可以配合Configuration、ImportSelector以及ImportBeanDefinitionRegistrar使用
//也可以把Import当成普通的bean来使用
public @interface Import {
Class<?>[] value();
}

注意:@Import注解只允许放到类上面,不允许放到方法上

@Import注解的使用方式

@Import注解的三种用法主要包括:

  1. 直接填写class数组的方式
  2. ImportSelector接口的方式,即批量导入,这是重点
  3. ImportBeanDefinitionRegistrar接口方式,即手工注册bean到容器中

首先来看第一种方式,创建一个Color类

1
2
public class Color {
}

在MainConfig2配置类上添加一个@Import注解,并将Color类填写到该注解中

1
2
3
4
5
6
7
8
// 对配置类中的组件进行统一设置
// 满足当前条件,这个类中配置的所有bean注册才能生效
@Conditional({WindowsCondition.class})
@Configuration
@Import(Color.class) // @Import快速地导入组件,id默认是组件的全类名
public class MainConfig2 {
...
}

@Import注解还支持同时导入多个类,比如又创建一个Red类

1
@Import({Color.class, Red.class})

ImportSelector

接下来学一下第二种方式,ImportSelector接口是Spring中导入外部配置
的核心接口,在Spring Boot的自动化配置和@EnableXXX(功能性注解)
都有它的存在

1
2
3
4
5
public interface ImportSelector {
/*这个参数能够获取到当前标注@Import注解的类的所有注解信息,也就是
说不仅能获取到@Import注解里面的信息,还能获取到其他注解的信息。*/
String[] selectImports(AnnotationMetadata var1);
}

其主要作用是收集需要导入的配置类,selectImports() 方法的返回值就
是我们向Spring 容器中导入的类的全类名。如果该接口的实现类同时实现
EnvironmentAware,BeanFactoryAware,BeanClassLoaderAware 或
者ResourceLoaderAware,那么在调用其selectImports()方法之前先
调用上述接口中对应的方法,如果需要在所有的@Configuration处理完
再导入时,那么可以实现DeferredImportSelector接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//创建一个MyImportSelector类实现ImportSelector接口
public class MyImportSelector implements ImportSelector {
// 返回值:就是要导入到容器中的组件的全类名
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
/*
至于使用MyImportSelector类要导入哪些bean,就需要你在MyImportSelector
类的selectImports()方法中进行设置了,只须在MyImportSelector类的
selectImports()方法中返回要导入的类的全类名(包名+类名)即可
*/

// 方法不要返回null值,否则会报空指针异常
//return new String[]{}; 可以返回一个空数组
return new String[]{"com.atguigu.bean.Bule",
"com.atguigu.bean.Yellow"};
}
}

然后在MainConfig2配置类的@Import注解中,导入MyImportSelector类

1
2
3
4
5
6
7
8
// 对配置类中的组件进行统一设置
@Conditional({WindowsCondition.class})
// 满足当前条件,这个类中配置的所有bean注册才能生效
@Configuration
@Import({Color.class, Red.class, MyImportSelector.class})
public class MainConfig2 {
...
}

ImportBeanDefinitionRegistrar

在该接口中,有一个registerBeanDefinitions()方法,通过该方法,我
们可以向Spring容器中注册bean实例

1
2
3
4
5
6
7
8
9
10
11
public interface ImportBeanDefinitionRegistrar {
/*
所有实现了该接口的类都会被ConfigurationClassPostProcessor处
理,ConfigurationClassPostProcessor实现了BeanFactoryPostProcessor
接口,所以ImportBeanDefinitionRegistrar中动态注册的bean是优先于依赖
其的bean初始化的,也能被aop、validator等机制处理
*/
public void registerBeanDefinitions(AnnotationMetadata
importingClassMetadata, BeanDefinitionRegistry registry) {
}
}

首先创建创建一个RainBow类,作为测试ImportBeanDefinitionRegistrar
接口的bean来使用

1
2
3
package com.atguigu.bean;
public class RainBow {
}

创建ImportBeanDefinitionRegistrar接口的实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class MyImportBeanDefinitionRegistrar implements 
ImportBeanDefinitionRegistrar {
/**
* AnnotationMetadata:当前类的注解信息
* BeanDefinitionRegistry:BeanDefinition注册类
*
* 我们可以通过调用BeanDefinitionRegistry接口中的registerBeanDefinition方
法,手动注册所有需要添加到容器中的bean
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata
importingClassMetadata, BeanDefinitionRegistry registry) {
boolean definition = registry.containsBeanDefinition(
"com.atguigu.bean.Red");
boolean definition2 = registry.containsBeanDefinition(
"com.atguigu.bean.Bule");
if (definition && definition2) {
// 指定bean的定义信息,包括bean的类型、作用域等等
// RootBeanDefinition是BeanDefinition接口的一个实现类
RootBeanDefinition beanDefinition = new RootBeanDefinition(
RainBow.class); // bean的定义信息
// 注册一个bean,并且指定bean的名称
registry.registerBeanDefinition("rainBow", beanDefinition);
}
}
}

以上registerBeanDefinitions()方法的实现逻辑很简单,就是判断Spring
容器中是否同时存在以Red命名的bean和以Bule命名的bean,如果真的同时
存在,那么向Spring容器中注入一个以rainBow命名的bean。
ImportBeanDefinitionRegistrar需要配合@Configuration和@Import
这俩注解,其中,@Configuration注解定义Java格式的Spring配置文件
,@Import注解导入实现了ImportBeanDefinitionRegistrar接口的类

1
2
3
4
5
6
7
8
9
10
// 对配置类中的组件进行统一设置
@Conditional({WindowsCondition.class})
// 满足当前条件,这个类中配置的所有bean注册才能生效
@Configuration
@Import({Color.class, Red.class, MyImportSelector.class,
MyImportBeanDefinitionRegistrar.class})
// @Import快速地导入组件,id默认是组件的全类名
public class MainConfig2 {
...
}

FactoryBean注册组件

可以使用FactoryBean向Spring容器中注册bean,一般情况下,Spring是
通过反射机制利用bean的class属性指定实现类来实例化bean的。在某些情
况下,实例化bean过程比较复杂,如果按照传统的方式,那么则需要在标
签中提供大量的配置信息,配置方式的灵活性是受限的,这时采用编码的
方式可以得到一个更加简单的方案。Spring为此提供了FactoryBean的工
厂类接口,用户可以通过实现该接口定制实例化bean的逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
/*返回由FactoryBean创建的bean实例,如果isSingleton()返回true
,那么该实例会放到Spring容器中单实例缓存池中*/
@Nullable
T getObject() throws Exception;
//返回FactoryBean创建的bean实例的类型
@Nullable
Class<?> getObjectType();
//返回由FactoryBean创建的bean实例的作用域是singleton还是prototype
default boolean isSingleton() {
return true;
}
}

需要注意的是:当配置文件中标签的class属性配置的实现类是FactoryBean
时,通过getBean()方法返回的不是FactoryBean本身,而是FactoryBean
#getObject()方法所返回的对象,相当于FactoryBean#getObject()代
理了getBean()方法

FactoryBean案例

创建一个ColorFactoryBean类,它得实现FactoryBean接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//创建一个Spring定义的FactoryBean
public class ColorFactoryBean implements FactoryBean<Color> {
// 返回一个Color对象,这个对象会添加到容器中
@Override
public Color getObject() throws Exception {
// TODO Auto-generated method stub
System.out.println("ColorFactoryBean...getObject...");
return new Color();
}
@Override
public Class<?> getObjectType() {
// TODO Auto-generated method stub
return Color.class; // 返回这个对象的类型
}
// 是单例吗?
// 如果返回true,那么代表这个bean是单实例,在容器中只会保存一份;
// 如果返回false,那么代表这个bean是多实例,每次获取都会创建一个新的bean
@Override
public boolean isSingleton() {
// TODO Auto-generated method stub
return false;
}
}

在MainConfig2配置类中加入ColorFactoryBean的声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 对配置类中的组件进行统一设置
@Conditional({WindowsCondition.class})
// 满足当前条件,这个类中配置的所有bean注册才能生效
@Configuration
@Import({Color.class, Red.class, MyImportSelector.class,
MyImportBeanDefinitionRegistrar.class})
// @Import快速地导入组件,id默认是组件的全类名
public class MainConfig2 {
...
@Bean
public ColorFactoryBean colorFactoryBean() {
return new ColorFactoryBean();
}
}

这里使用@Bean注解向Spring容器中注册的是ColorFactoryBean对象,但
是实际上从Spring容器中获取到的bean对象却是调用ColorFactoryBean
类中的getObject()方法获取到的Color对象

1
2
3
4
5
6
7
8
9
10
11
12
@Test
public void testImport() {
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(MainConfig2.class);
String[] definitionNames = applicationContext.getBeanDefinitionNames();
for (String name : definitionNames) {
System.out.println(name);
}
// 工厂bean获取的是调用getObject方法创建的对象
Object bean2 = applicationContext.getBean("colorFactoryBean");
System.out.println("bean的类型:" + bean2.getClass());
}

获取到FactoryBean对象本身

只需要在获取工厂Bean本身时,在id前面加上&符号即可,例如&colorFactoryBean

1
2
3
4
5
6
7
8
9
10
public void testImport() {
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(MainConfig2.class);
String[] definitionNames = applicationContext.getBeanDefinitionNames();
for (String name : definitionNames) {
System.out.println(name);
}
Object bean4 = applicationContext.getBean("&colorFactoryBean");
System.out.println(bean4.getClass());
}

@Bean注解指定初始化和销毁的方法

接下来是有关bean的生命周期的学习

bean的生命周期

通常意义上讲的bean的生命周期,指的是bean从创建到初始化,经过一系
列的流程,最终销毁的过程。在Spring中,bean 的生命周期是由Spring
容器来管理的。在Spring中,我们可以自己来指定bean 的初始化和销毁
的方法。我们指定了bean的初始化和销毁方法之后,当容器在bean进行
到当前生命周期的阶段时,会自动调用我们自定义的初始化和销毁方法

定义初始化和销毁方法

第一种定义初始化和销毁方法的方式:通过@Bean注解指定初始化和销毁方
法。如果是使用XML配置文件的方式配置bean的话,那么可以在标签中指定
bean的初始化和销毁方法

1
2
3
4
5
<bean id="person" class="com.meimeixia.bean.Person" init-method="init" 
destroy-method="destroy">
<property name="age" value="18"></property>
<property name="name" value="liayun"></property>
</bean>

在我们自己写的Person类中,需要存在init()方法和destroy()方法。而
且Spring中还规定,这里的init()方法和destroy()方法必须是无参方
法,但可以抛出异常,首先创建一个Person类

1
2
3
4
5
6
7
8
9
10
11
public class Car {
public Car() {
System.out.println("car constructor...");
}
public void init() {
System.out.println("car ... init...");
}
public void destroy() {
System.out.println("car ... destroy...");
}
}

然后将Car类对象通过注解的方式注册到Spring容器中,具体的做法就
是新建一个MainConfigOfLifeCycle类作为Spring的配置类,将Car
类对象通过MainConfigOfLifeCycle类注册到Spring容器中

1
2
3
4
5
6
7
@Configuration
public class MainConfigOfLifeCycle {
@Bean
public Car car() {
return new Car();
}
}

接着新建一个IOCTest_LifeCycle类来测试容器中的Car对象

1
2
3
4
5
6
7
8
9
public class IOCTest_LifeCycle {
@Test
public void test01() {
// 1. 创建IOC容器
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
System.out.println("容器创建完成");
}
}

对于单实例bean对象来说,在Spring 容器创建完成后,就会对单实例
bean进行实例化,此时没有执行init和destroy方法,如果是使用XML
文件配置的话,那么我们可以使用如下配置来实现

1
2
3
<bean id="car" class="com.meimeixia.bean.Car" init-method="init" 
destroy-method="destroy">
</bean>

接下来使用@Bean注解实现

1
2
3
4
5
6
7
8
9
10
11
public @interface Bean {
@AliasFor("name")
String[] value() default {};
@AliasFor("value")
String[] name() default {};
@Deprecated
Autowire autowire() default Autowire.NO;
boolean autowireCandidate() default true;
String initMethod() default "";
String destroyMethod() default "(inferred)";
}

使用@Bean注解的initMethod属性和destroyMethod属性来指定bean的
初始化方法和销毁方法

1
2
3
4
5
6
7
@Configuration
public class MainConfigOfLifeCycle {
@Bean(initMethod="init", destroyMethod="destroy")
public Car car() {
return new Car();
}
}

在Spring容器中,先是调用了Car类的构造方法来创建Car对象,接下来便
是调用了Car对象的init()方法来进行初始化。但是不会打印销毁信息,
bean的销毁方法是在容器关闭的时候被调用的

1
2
3
4
5
6
7
8
9
10
11
public class IOCTest_LifeCycle {
@Test
public void test01() {
// 1. 创建IOC容器
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
System.out.println("容器创建完成");
// 关闭容器
applicationContext.close();
}
}

初始化和销毁方法的使用场景

一个典型的使用场景就是对于数据源的管理。在配置数据源时,在初始化
的时候,会对很多的数据源的属性进行赋值操作,在销毁的时候,我们
需要对数据源的连接等信息进行关闭和清理。这个时候,就可以在自定
义的初始化和销毁方法中来做这些事情了

初始化和销毁方法调用的时机

  1. bean对象的初始化方法调用的时机:对象创建完成,如果对象中存在
    一些属性,并且这些属性也都赋好值之后,那么就会调用bean的初始化
    方法。对于单实例bean来说,在Spring容器创建完成后,Spring 容器
    会自动调用bean 的初始化方法,对于多实例bean 来说,在每次获取
    bean对象的时候,调用bean的初始化方法
  2. bean对象的销毁方法调用的时机:对于单实例bean来说,在容器关闭
    的时候,会调用bean的销毁方法,对于多实例bean来说,Spring容器不
    会管理这个bean,也就不会自动调用这个bean的销毁方法了

多实例Bean

多实例bean的初始化和销毁方法调用的时机与单实例不同

1
2
3
4
5
6
7
8
@Configuration
public class MainConfigOfLifeCycle {
@Scope("prototype")
@Bean(initMethod="init", destroyMethod="destroy")
public Car car() {
return new Car();
}
}

当我们在获取多实例bean对象的时候,会创建对象并进行初始化,多实
例的bean在容器关闭的时候是不进行销毁的,也就是说你每次获取时,
IOC容器帮你创建出对象交还给你,至于要什么时候销毁这是你自己的
事,Spring容器压根就不会再管理这些多实例的bean了

管理bean的生命周期

Spring中提供了一个InitializingBean接口,该接口为bean提供了属性
初始化后的处理方法,它只包括afterPropertiesSet方法,凡是继承该
接口的类,在bean的属性初始化后都会执行该方法

1
2
3
4
public interface InitializingBean {
//afterPropertiesSet()方法是在属性赋好值之后调用的。
void afterPropertiesSet() throws Exception;
}

何时调用InitializingBean接口

首先定位到AbstractAutowireCapableBeanFactory这个类里面的
invokeInitMethods()方法中,来查看Spring加载bean的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
protected void invokeInitMethods(String beanName, Object bean, 
@Nullable RootBeanDefinition mbd) throws Throwable {
//判断bean是否实现了InitializingBean,如果实现了就调用afterPropertiesSet
boolean isInitializingBean = bean instanceof InitializingBean;
if (isInitializingBean && (mbd == null ||
!mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (this.logger.isTraceEnabled()) {
this.logger.trace(
"Invoking afterPropertiesSet() on bean with name '"
+ beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged(() -> {
//调用bean的afterPropertiesSet
((InitializingBean)bean).afterPropertiesSet();
return null;
}, this.getAccessControlContext());
} catch (PrivilegedActionException var6) {
throw var6.getException();
}
} else {
//调用bean的afterPropertiesSet
((InitializingBean)bean).afterPropertiesSet();
}
}
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) && (!isInitializingBean
|| !"afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
//通过反射的方式调用init-method
this.invokeCustomInitMethod(beanName, bean, mbd);
}
}
}

分析上述代码后,我们可以初步得出如下信息

  1. Spring为bean提供了两种初始化的方式,实现InitializingBean
    接口(也就是要实现该接口中的afterPropertiesSet方法),或者在
    配置文件或@Bean注解中通过init-method来指定,两种方式可以同时
    使用
  2. 实现InitializingBean接口是直接调用afterPropertiesSet()方
    法,与通过反射调用init-method指定的方法相比,效率相对来说要高
    点。但是init-method方式消除了对Spring的依赖
  3. 如果调用afterPropertiesSet方法时出错,那么就不会调用
    init-method指定的方法了

也就是说Spring 为bean 提供了两种初始化的方式,第一种方式是实现
InitializingBean 接口(实现该接口中afterPropertiesSet方法)
,第二种方式是在配置文件或@Bean注解中通过init-method来指定,
这两种方式可以同时使用,同时使用先调用afterPropertiesSet方
法,后执行init-method 指定的方法

DisposableBean接口

实现DisposableBean 接口的bean在销毁前,Spring将会调用该接口
的destroy()方法

1
2
3
public interface DisposableBean {
void destroy() throws Exception;
}

在bean生命周期结束前调用destroy()方法做一些收尾工作,亦可以使用
destroy-method。前者与Spring耦合高,使用类型强转.方法名(),效
率高,后者耦合低,使用反射,效率相对来说较低

DisposableBean接口注意事项

多实例bean的生命周期不归Spring容器来管理,这里的DisposableBean
接口中的方法是由Spring容器来调用的,所以如果一个多实例bean 实现
了DisposableBean接口是没有啥意义的,因为相应的方法根本不会被调
用,在XML配置文件中指定了destroy方法,也是没有任何意义的。在多
实例bean情况下,Spring是不会自动调用bean的销毁方法的

单实例bean

创建一个Cat的类来实现InitializingBean和DisposableBean这俩接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Component
public class Cat implements InitializingBean, DisposableBean {
public Cat() {
System.out.println("cat constructor...");
}
/**
* 会在容器关闭的时候进行调用
*/
@Override
public void destroy() throws Exception {
// TODO Auto-generated method stub
System.out.println("cat destroy...");
}
/**
* 会在bean创建完成,并且属性都赋好值以后进行调用
*/
@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
System.out.println("cat afterPropertiesSet...");
}
}

在MainConfigOfLifeCycle配置类中通过包扫描的方式将以上类注入
到Spring容器中

1
2
3
4
5
6
7
8
9
@ComponentScan("com.meimeixia.bean")
@Configuration
public class MainConfigOfLifeCycle {
@Scope("prototype")
@Bean(initMethod="init", destroyMethod="destroy")
public Car car() {
return new Car();
}
}

单实例bean情况下,IOC容器创建完成后,会自动调用bean的初始化方
法,而在容器销毁前,会自动调用bean的销毁方法

多实例bean

在多实例bean情况下,Spring不会自动调用bean的销毁方法

1
2
3
4
5
@Scope("prototype")
@Component
public class Cat implements InitializingBean, DisposableBean {
...
}

@PostConstruct注解和@PreDestroy注解

在JDK中还提供了两个注解能够在bean 创建完成并且属性赋值完成之后
执行一些初始化工作和在容器销毁bean之前通知我们进行一些清理工作

@PostConstruct注解

@PostConstruct注解好多人以为是Spring提供的,其实它是Java自己的
注解,是JSR-250规范里面定义的一个注解

1
2
3
4
5
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface PostConstruct {
}

该注解被用来修饰一个非静态的void()方法。被@PostConstruct注解修饰
的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。
被@PostConstruct注解修饰的方法通常在构造函数之后,init()方法之
前执行,执行顺序如下

1
Constructor(构造方法)→@Autowired(依赖注入)→@PostConstruct(注释的方法)

@PreDestroy注解

@PreDestroy注解同样是Java提供的,它也是JSR-250规范里面定义的一
个注解

1
2
3
4
5
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface PreDestroy {
}

被@PreDestroy 注解修饰的方法会在服务器卸载Servlet 的时候运行,并且
只会被服务器调用一次,类似于Servlet的destroy()方法。被@PreDestroy
注解修饰的方法会在destroy()方法之后,Servlet被彻底卸载之前执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Component
public class Dog {
public Dog() {
System.out.println("dog constructor...");
}
// 在对象创建完成并且属性赋值完成之后调用
@PostConstruct
public void init() {
System.out.println("dog...@PostConstruct...");
}
// 在容器销毁(移除)对象之前调用
@PreDestroy
public void destory() {
System.out.println("dog...@PreDestroy...");
}
}

BeanPostProcessor后置处理器

BeanPostProcessor是一个接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface BeanPostProcessor {
@Nullable
//在bean实例化和属性设置之后,自定义初始化方法之前被调用
default Object postProcessBeforeInitialization(Object bean, String
beanName) throws BeansException {
return bean;
}
@Nullable
//在自定义初始化方法之后被调用
default Object postProcessAfterInitialization(Object bean, String
beanName) throws BeansException {
return bean;
}
}

这两个方法分别是在Spring容器中的bean初始化前后执行,所以Spring
容器中的每一个bean对象初始化前后,都会执行BeanPostProcessor接
口的实现类中的这两个方法。当容器中存在多个BeanPostProcessor的
实现类时,会按照它们在容器中注册的顺序执行。对于自定义的实现类
,还可以让其实现Ordered接口自定义排序

后置处理器实例

创建一个MyBeanPostProcessor类,实现BeanPostProcessor接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Component // 将后置处理器加入到容器中,这样的话,Spring就能让它工作了
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String
beanName) throws BeansException {
// TODO Auto-generated method stub
System.out.println("postProcessBeforeInitialization..." + beanName +
"=>" + bean);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String
beanName) throws BeansException {
// TODO Auto-generated method stub
System.out.println("postProcessAfterInitialization..." + beanName +
"=>" + bean);
return bean;
}
}

也可以让我们自己写的MyBeanPostProcessor类来实现Ordered接
口自定义排序

1
2
3
4
5
6
7
8
9
@Component // 将后置处理器加入到容器中,这样的话,Spring就能让它工作了
public class MyBeanPostProcessor implements BeanPostProcessor, Ordered {
...
@Override
public int getOrder() {
// TODO Auto-generated method stub
return 3;
}
}

后置处理器作用

后置处理器可用于bean 对象初始化前后进行逻辑增强。Spring 提供了
该接口很多实现类

  1. AutowiredAnnotationBeanPostProcessor用于@Autowired注解
    的实现
  2. AnnotationAwareAspectJAutoProxyCreator用于Spring AOP
    的动态代理等等

我们都知道spring AOP的实现原理是动态代理,最终放入容器的是代理
类的对象,而不是bean本身的对象,那么Spring是什么时候做到这一步
的呢?就是在AnnotationAwareAspectJAutoProxyCreator后置处理
器的postProcessAfterInitialization方法中,即bean对象初始化
完成之后,后置处理器会判断该bean是否注册了切面,若是,则生成
代理对象注入到容器中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public abstract class AbstractAutoProxyCreator extends 
ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor,
BeanFactoryAware {
...
public Object postProcessAfterInitialization(@Nullable Object
bean, String beanName) {
if (bean != null) {
Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return this.wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
}

BeanPostProcessor的执行流程

接下来看一下BeanPostProcessor的底层原理

bean的初始化和销毁

bean的初始化和销毁方法我们可以通过如下四种方式进行指定

  1. 通过@Bean指定init-method和destroy-method
    1
    2
    3
    4
    @Bean(initMethod="init", destroyMethod="destroy")
    public Car car() {
    return new Car();
    }
  2. 通过让bean实现InitializingBean和DisposableBean这俩接口
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @Component
    public class Cat implements InitializingBean, DisposableBean {
    public Cat() {
    System.out.println("cat constructor...");
    }
    @Override
    public void destroy() throws Exception {
    // TODO Auto-generated method stub
    System.out.println("cat destroy...");
    }
    @Override
    public void afterPropertiesSet() throws Exception {
    // TODO Auto-generated method stub
    System.out.println("cat afterPropertiesSet...");
    }
    }
  3. 使用JSR-250规范里面定义的@PostConstruct和@PreDestroy
  • @PostConstruct:在bean创建完成并且属性赋值完成之后,来执行
    初始化方法
  • @PreDestroy:在容器销毁bean之前通知我们进行清理工作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Component
public class Dog {
public Dog() {
System.out.println("dog constructor...");
}
// 在对象创建完成并且属性赋值完成之后调用
@PostConstruct
public void init() {
System.out.println("dog...@PostConstruct...");
}
// 在容器销毁(移除)对象之前调用
@PreDestroy
public void destory() {
System.out.println("dog...@PreDestroy...");
}
}
  1. 通过让bean实现BeanPostProcessor接口
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    @Component // 将后置处理器加入到容器中,这样的话,Spring就能让它工作了
    public class MyBeanPostProcessor implements BeanPostProcessor, Ordered {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String
    beanName) throws BeansException {
    // TODO Auto-generated method stub
    System.out.println("postProcessBeforeInitialization..." + beanName +
    "=>" + bean);
    return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String
    beanName) throws BeansException {
    // TODO Auto-generated method stub
    System.out.println("postProcessAfterInitialization..." + beanName +
    "=>" + bean);
    return bean;
    }
    @Override
    public int getOrder() {
    // TODO Auto-generated method stub
    return 3;
    }
    }

通过以上这四种方式,我们就可以对bean的整个生命周期进行控制:

  1. bean的实例化:调用bean的构造方法,我们可以在bean的无参构造
    方法中执行相应的逻辑
  2. bean的初始化:在初始化时,可以通过BeanPostProcesso 的两
    个方法进行拦截,执行自定义的逻辑,通过@PostConstruct 注解、
    InitializingBean和init-method来指定bean初始化前后执行的
    方法,在该方法中咱们可以执行自定义的逻辑
  3. bean的销毁:可以通过@PreDestroy注解、DisposableBean和
    destroy-method来指定bean在销毁前执行的方法,在该方法中咱
    们可以执行自定义的逻辑

BeanPostProcessor源码解析

AbstractAutowireCapableBeanFactory类的doCreateBean()方法中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, 
@Nullable Object[] args) throws BeanCreationException {
...
Object exposedObject = bean;
try {
this.populateBean(beanName, mbd, instanceWrapper);
//定位到这里
exposedObject = this.initializeBean(beanName, exposedObject, mbd);
} catch (Throwable var18) {
if (var18 instanceof BeanCreationException && beanName.equals(
((BeanCreationException)var18).getBeanName())) {
throw (BeanCreationException)var18;
}
throw new BeanCreationException(mbd.getResourceDescription(),
beanName, "Initialization of bean failed", var18);
}
...
}

调用initializeBean()方法之前,还调用了一个populateBean()方
法,populateBean() 方法同样是该类中的方法,它里面的代码比较
多,但是逻辑非常简单,populateBean()方法做的工作就是为bean
的属性赋值。也就是说,在Spring中会先调用populateBean()方法
为bean的属性赋好值,然后再调用initializeBean()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
protected Object initializeBean(String beanName, Object bean, 
@Nullable RootBeanDefinition mbd) {
...
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
//在调用invokeInitMethods()方法之前,Spring调用了这个方法
wrappedBean = this.applyBeanPostProcessorsBeforeInitialization(
bean, beanName);
}
try {
//注意这里
/*
invokeInitMethods() 方法的作用就是执行初始化方法,这些初始
化方法包括我们之前讲的:在XML配置文件标签中使用init-method
属性指定初始化方法,在@Bean注解中使用initMehod属性指定的方
法,使用@PostConstruct注解标注方法,实现InitializingBean
接口的方法等。
*/
this.invokeInitMethods(beanName, wrappedBean, mbd);
} catch (Throwable var6) {
throw new BeanCreationException(mbd != null ?
mbd.getResourceDescription() : null, beanName,
"Invocation of init method failed", var6);
}
if (mbd == null || !mbd.isSynthetic()) {
//在调用invokeInitMethods()方法之后,Spring又调用了这个方法
wrappedBean = this.applyBeanPostProcessorsAfterInitialization(
wrappedBean, beanName);
}
return wrappedBean;
}

先来看看applyBeanPostProcessorsBeforeInitialization()
方法中具体执行了哪些逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public Object applyBeanPostProcessorsBeforeInitialization(Object 
existingBean, String beanName) throws BeansException {
Object result = existingBean;
Object current;
/*
会遍历所有BeanPostProcessor对象,然后依次执行所有
BeanPostProcessor对象的postProcessBeforeInitialization()
方法,一旦BeanPostProcessor对象的postProcessBeforeInitialization()
方法返回null以后,则后面的BeanPostProcessor对象便不再执行了,而是直接
退出for循环
*/
for(Iterator var4 = this.getBeanPostProcessors().iterator();
var4.hasNext(); result = current) {
BeanPostProcessor processor = (BeanPostProcessor)var4.next();
current = processor.postProcessBeforeInitialization(result,
beanName);
if (current == null) {
return result;
}
}
return result;
}

经过上面的一系列的跟踪源码分析,我们可以将关键代码的调用过程使用
如下伪代码表述出来

1
2
3
4
5
6
7
populateBean(beanName, mbd, instanceWrapper); // 给bean进行属性赋值
initializeBean(beanName, exposedObject, mbd)
{
applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
invokeInitMethods(beanName, wrappedBean, mbd); // 执行自定义初始化
applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}

也就是说,在Spring中,调用initializeBean()方法之前,调用了
populateBean()方法为bean的属性赋值,为bean的属性赋好值之后
,再调用initializeBean()方法进行初始化。
在initializeBean()中,调用自定义初始化方法invokeInitMethods()
之前,调用了applyBeanPostProcessorsBeforeInitialization()方
法,applyBeanPostProcessorsAfterInitialization()方法在调用
自定义的初始化方法后调用,至此整个bean的初始化过程就这样结束了

BeanPostProcessor底层使用

依然来看一下BeanPostProcessor的源码,接下来就来分析下
BeanPostProcessor接口在Spring中的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface BeanPostProcessor {
//在bean初始化之前调用
@Nullable
default Object postProcessBeforeInitialization(Object bean, String
beanName) throws BeansException {
return bean;
}
//bean初始化之后调用
@Nullable
default Object postProcessAfterInitialization(Object bean, String
beanName) throws BeansException {
return bean;
}
}

ApplicationContextAwareProcessor类

ApplicationContextAwareProcessor是BeanPostProcessor接
口的一个实现类,这个类的作用是可以向组件中注入IOC容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
class ApplicationContextAwareProcessor implements BeanPostProcessor {
private final ConfigurableApplicationContext applicationContext;
private final StringValueResolver embeddedValueResolver;
public ApplicationContextAwareProcessor(ConfigurableApplicationContext
applicationContext) {
this.applicationContext = applicationContext;
this.embeddedValueResolver = new EmbeddedValueResolver(
applicationContext.getBeanFactory());
}
@Nullable
public Object postProcessBeforeInitialization(Object bean, String
beanName) throws BeansException {
/*
在bean初始化之前,首先对当前bean的类型进行判断,如果当前bean
的类型不是EnvironmentAware,不是EmbeddedValueResolverAware
,不是ResourceLoaderAware,不是ApplicationEventPublisherAware
,不是MessageSourceAware,也不是ApplicationContextAware,
那么直接返回bean。如果是上面类型中的一种类型,那么最终会调用
invokeAwareInterfaces()方法,并将bean传递给该方法
*/
if (!(bean instanceof EnvironmentAware) && !(bean
instanceof EmbeddedValueResolverAware) && !(bean
instanceof ResourceLoaderAware) && !(bean
instanceof ApplicationEventPublisherAware) && !(bean
instanceof MessageSourceAware) && !(bean
instanceof ApplicationContextAware)) {
return bean;
} else {
AccessControlContext acc = null;
if (System.getSecurityManager() != null) {
acc = this.applicationContext.getBeanFactory().
getAccessControlContext();
}

if (acc != null) {
AccessController.doPrivileged(() -> {
this.invokeAwareInterfaces(bean);
return null;
}, acc);
} else {
//注意这里
this.invokeAwareInterfaces(bean);
}

return bean;
}
}
/*
判断当前bean属于哪种接口类型,然后将bean强转为哪种接口类型的对象
,接着调用接口中的方法,将相应的参数传递到接口的方法中

*/
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware)bean).setEnvironment(
this.applicationContext.getEnvironment());
}

if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware)bean).setEmbeddedValueResolver(
this.embeddedValueResolver);
}

if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware)bean).setResourceLoader(
this.applicationContext);
}

if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware)bean).
setApplicationEventPublisher(this.applicationContext);
}

if (bean instanceof MessageSourceAware) {
((MessageSourceAware)bean).setMessageSource(this.
applicationContext);
}

if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware)bean).setApplicationContext(
this.applicationContext);
}
}
}

要想使用ApplicationContextAwareProcessor类向组件中注
入IOC 容器,我们就不得不提Spring 中的另一个接口了,即
ApplicationContextAware。如果需要向组件中注入IOC容器
,那么可以让组件实现ApplicationContextAware接口。
创建一个Dog类,使其实现ApplicationContextAware接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/*
ApplicationContextAwareProcessor这个类的作用是可以帮我们在组件里面注入
IOC容器,怎么注入呢?我们想要IOC容器的话,比如我们这个Dog组件,只需要实现
ApplicationContextAware接口就行
*/
@Component
public class Dog implements ApplicationContextAware {
private ApplicationContext applicationContext;

public Dog() {
System.out.println("dog constructor...");
}

// 在对象创建完成并且属性赋值完成之后调用
@PostConstruct
public void init() {
System.out.println("dog...@PostConstruct...");
}

// 在容器销毁(移除)对象之前调用
@PreDestroy
public void destory() {
System.out.println("dog...@PreDestroy...");
}
/*
需要实现ApplicationContextAware接口中的setApplicationContext()方法
有一个ApplicationContext类型的参数,这个就是IOC容器对象,我们可以在
Dog类中定义一个ApplicationContext类型的成员变量,然后在
setApplicationContext()方法中为这个成员变量赋值,此时就
可以在Dog类中的其他方法中使用ApplicationContext对象了
*/
@Override
public void setApplicationContext(ApplicationContext
applicationContext) throws BeansException {
// TODO Auto-generated method stub
this.applicationContext = applicationContext;
}
}

BeanValidationPostProcessor类

该类主要是用来为bean进行校验操作的,当我们创建bean,并为bean
赋值后,我们可以通过BeanValidationPostProcessor类为bean进
行校验操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class BeanValidationPostProcessor implements 
BeanPostProcessor, InitializingBean {
@Nullable
private Validator validator;
private boolean afterInitialization = false;
...
/*
调用doValidate()方法对bean进行校验,只不过在这两个方法中都会对
afterInitialization这个boolean类型的成员变量进行判断
*/
public Object postProcessBeforeInitialization(Object bean, String
beanName) throws BeansException {
if (!this.afterInitialization) {
this.doValidate(bean);
}

return bean;
}

public Object postProcessAfterInitialization(Object bean, String
beanName) throws BeansException {
if (this.afterInitialization) {
this.doValidate(bean);
}

return bean;
}
}

InitDestroyAnnotationBeanPostProcessor类

该类主要用来处理@PostConstruct注解和@PreDestroy注解,之
前创建的Dog类中就使用了@PostConstruct注解和@PreDestroy
注解。
在Dog类中使用了@PostConstruct注解和@PreDestroy注解来标注
方法,Spring怎么就知道什么时候执行@PostConstruct注解标注
的方法,什么时候执行@PreDestroy注解标注的方法呢?这就要归
功于InitDestroyAnnotationBeanPostProcessor类了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class InitDestroyAnnotationBeanPostProcessor 
implements DestructionAwareBeanPostProcessor,
MergedBeanDefinitionPostProcessor, PriorityOrdered, Serializable {

/*
在进入使用@PostConstruct注解标注的方法之前,Spring调用了
InitDestroyAnnotationBeanPostProcessor类的
postProcessBeforeInitialization()方法
*/
public Object postProcessBeforeInitialization(Object bean, String
beanName) throws BeansException {
/*
首先会找到bean中有关生命周期的注解,比如@PostConstruct
注解等,找到这些注解之后,则将这些信息赋值给LifecycleMetadata
类型的变量metadata,之后调用metadata的invokeInitMethods()方
法,通过反射来调用标注了@PostConstruct注解的方法。这就是为什么
标注了@PostConstruct注解的方法会被Spring执行的原因
*/
InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata metadata =
this.findLifecycleMetadata(bean.getClass());
try {
metadata.invokeInitMethods(bean, beanName);
return bean;
} catch (InvocationTargetException var5) {
throw new BeanCreationException(beanName,
"Invocation of init method failed", var5.getTargetException());
} catch (Throwable var6) {
throw new BeanCreationException(beanName,
"Failed to invoke init method", var6);
}
}
}

AutowiredAnnotationBeanPostProcessor类

该类主要是用于处理标注了@Autowired注解的变量或方法

Author: 高明
Link: https://skysea-gaoming.github.io/2021/04/17/Spring2/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.