实战:详解Spring创建bean的流程(图解+示例+源码)

概叙

这篇主要总结Spring中bean的创建过程,主要分为==加载bean信息–>实例化bean–>属性填充–>初始化阶段–>后置处理等步骤,且每个步骤Spring做的事情都很多,这块源码还是很值得我们都去看一看的。==而Spring中Bean的声明周期其实就是创建到使用到销毁,使用应该没啥需要说的,销毁在第三部分也正常介绍了三种销毁的方式。

如不想看文字,可以直接看后面的流程图。

什么是 Bean?

我们来看下 Spring Framework 的官方文档:

In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans. A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container. Otherwise, a bean is simply one of many objects in your application. Beans, and the dependencies among them, are reflected in the configuration metadata used by a container.

简而言之,bean 是由 Spring IoC 容器实例化、组装和管理的对象。

什么是 Spring Bean 的生命周期?

对于普通的 Java 对象,当 new 的时候创建对象,然后该对象就能够使用了。一旦该对象不再被使用,则由 Java 自动进行垃圾回收。

而 Spring 中的对象是 bean,bean 和普通的 Java 对象没啥大的区别,只不过 Spring 不再自己去 new 对象了,而是由 IoC 容器去帮助我们实例化对象并且管理它,我们需要哪个对象,去问 IoC 容器要即可。IoC 其实就是解决对象之间的耦合问题,Spring Bean 的生命周期完全由容器控制。

Spring创建bean的详细流程

上面的流程图其实已经可以很清晰的看到bean的创建过程了,这里结合图片我们一起来详细说下这个过程,这里不贴源码,贴了源码只会让观看的人比较迷糊,若是想跟源码的可以对照上面的流程图完全能做到源码复现,bean创建的这个过程大致可以分为五步:加载bean信息,实例化bean,bean属性填充,初始化bean,后置操作,那我们就基于这五大步来看看Spring是如何创建bean的。

1.加载bean信息

被IOC注解修饰的类,或者通过xml配置的类,首先在容器启动时一refresh方法为入口,会将这些类扫描进来形成BeanDefinition信息,BeanDefinition就是包含了我们配置的一些bean的属性,比如是否单例,是否有bean依赖(DependOn),bean的名称,bean所属class的全路径等,这里存储的相当于bean的元信息,然后通过 BeanDefinitionRegistry将这些BeanDefinition加载进来后面我们就可以利用该信息了,且在Spring创建Bean的全程都需要BeanDefinition的参与,所以他很重要。

2.实例化bean

通过上面的图可以清晰看到在实例化阶段之前其实还有很多小的操作:容器会先去尝试getBean–>doGetBean–>getSingleton等操作在这些操作都拿不到对象以后才会开始着手创建对象,需要说的是getSingleton会尝试从三级缓存中依次去获取Bean,当所有缓存都获取不到时就可以确认当前bean没有被创建,然后就可以启动创建的相关动作

  • 利用BeanDefinition检查是否有依赖的bean(配置了@DependOn注解)如有,需要先加载依赖bean
  • 利用BeanDefinition检查是否单例bean,是走单例bean的创建流程,不是再判断是否是原型bean,是走原型bean创建,否则都是另一套路径创建
  • 开始实例化,调用getSingleton,此时传入的是对象工厂(ObjectFactory)的实现类,因为对象工厂是函数式接口,这里传入的其实就是createBean‘的lamda表达式
  • 将当前bean加入到正在创建bean的一个set
  • 调用对象工厂的getObject方法,因为我们再上面已经传入了对象工厂(通过lamda表达式传入)这里相当于调用刚刚的lamda表达式,调用里面的createBean方法
  • createBean去调了doCreateBean又调了createBeanInstance,在这里底层通过反射技术获取构造参数将对象创建了出来,此时的对象只是通过空参构造创建出来的对象,他并没有任何的属性。
  • 调用addSingletonFactory将实例化完成的bean加入到三级缓存,到这里实例化就算是结束了

3.bean属性填充

属性填充其实就为自身属性进行赋值的过程,根据我们的DI注解这里会先从三个缓存中获取bean,若是获取不到,则会尝试进行bean的创建,若是走到了bean的创建,则会重新走一边bean创建的整个流程,这里是递归逻辑。

  • populateBean 该方法是填充属性的入口,传入beanName和BeanDefinition
  • 从BeanDefinition中获取属性注入相关信息然后判断是名称注入还是类型注入
  • 调用getSingleton从容器中获取所需对象,若是获取不到则会重走对象创建的整个流程,拿到完整对象后将其给到当前bean的属性,到这里属性填充就结束了

4.初始化bean

属性填充完毕后并没有立即结束这个过程,还有一些其他的操作需要spring进行处理,比如aware接口的处理,postprocessor接口的处理,初始化的处理等操作其实这里主要就是处理这三个动作的

  • 判断有无实现aware接口,如有则去执行他的实现类的实现方法,所有aware接口可以参考上图中所列的三个aware接口,在spring初始化时会对他们进行是否实现的判断
  • 获取容器中所有postprocessor接口,然后开始执行他的前置方法
  • 判断有无实现初始化接口InitializingBean如有则去执行初始化方法afterPropertiesSet
  • 执行postprocessor的后置方法,通过前置和后置方法我们可以实现自定义的一些逻辑,不过需要注意的是这些前置和后置方法会作用到所有bean

5.后置操作

这里的后置操作,主要是完成一些清扫工作和适配工作,比如删除二级、三级缓存中无用的bean引用等,下面是具体操作。

  • 将bean从创建中的集合中删除
  • 将bean加入到单例池中将其从二级三级缓存中删除
  • 对对象进行一些适配操作,到这里完成了初始化的所有操作,后面就是一步步返回调用的地方了

看了这五步,不知道是不是对bean的创建过程有了清晰的认识,如果还是不够清晰可以根据第一部分的流程图走下代码,代码走两遍其实就会比较清晰了。

bean的生命周期

这里我们说的 Spring Bean 的生命周期主要指的是 singleton bean,对于 prototype 的 bean ,Spring 在创建好交给使用者之后则不会再管理后续的生命周期。

我们也来复习下 Spring 中的 bean 的作用域有哪些?

singleton : 唯一 bean 实例,Spring 中的 bean 默认都是单例的。
prototype : 每次请求都会创建一个新的 bean 实例。
request : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP request 内有效。
session : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP session 内有效。
global-session: 全局 session 作用域,仅仅在基于 Portlet 的 web 应用中才有意义,Spring5 已经没有了。Portlet 是能够生成语义代码(例如:HTML)片段的小型 Java Web 插件。它们基于 portlet 容器,可以像 servlet 一样处理 HTTP 请求。但是,与 servlet 不同,每个 portlet 都有不同的会话。

我们知道对于普通的 Java 对象来说,它们的生命周期就是:

  • 实例化
  • 该对象不再被使用时通过垃圾回收机制进行回收

而对于 Spring Bean 的生命周期来说:

  • 实例化 Instantiation
  • 属性赋值 Populate
  • 初始化 Initialization
  • 销毁 Destruction

实例化 -> 属性赋值 -> 初始化 -> 销毁

只有四个步骤,这样拆解的话是不是感觉也不难?不像其他人写的那样直接一上来就各种 BeanPostProcessor、BeanFactoryPostProcessor 全部怼进流程里去,别说读者看着头大,自己写的可能短时间内还记得流程,隔个一段时间,你可能都不知道自己写了个啥。

本来小编想通过 Bean 创建流程入口

AbstractApplicationContext#refresh() 方法的 finishBeanFactoryInitialization(beanFactory) 处带大家跟一下源码,想了想还是不带入过多的代码进来,直接给到最终的主要逻辑。

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

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {

    BeanWrapper instanceWrapper = null;

    if (mbd.isSingleton()) {

        instanceWrapper = (BeanWrapper)this.factoryBeanInstanceCache.remove(beanName);

    }

    if (instanceWrapper == null) {

        // 实例化阶段

        instanceWrapper = this.createBeanInstance(beanName, mbd, args);

    }

    ...

    Object exposedObject = bean;

    try {

        // 属性赋值阶段

        this.populateBean(beanName, mbd, instanceWrapper);

        // 初始化阶段

        exposedObject = this.initializeBean(beanName, exposedObject, mbd);

    } catch (Throwable var18) {

        ...

    }

    ...

}

至于销毁,是在容器关闭时调用的,详见 ConfigurableApplicationContext#close()

是不是很清爽了?至于 BeanPostProcessor、BeanFactoryPostProcessor 以及其他的类,在老周看来,只不过是对主流程四个步骤的一系列扩展点而已。

bean的生命周期其实就是从创建到销毁,上面创建已经说完了,其实只差销毁这一步了。bean销毁发生在容器关闭时对单例bean进行清除操作。在Spring中我们通常有三种方式定义bean销毁时的逻辑

1.通过PreDestroy注解修饰方法

Bean销毁时会检查有无该注解修饰的方法,如有,会对该注解修饰的方法进行执行

2.通过指定destroy-method方法

在使用xml对bean进行注入时,我们可以指定init-method方法,也可以指定destroy-method方法,同样的使用Bean注解时也是支持这两个属性的,Spring容器关闭时会寻找当前bean有无指定destroy-method,如有则会进行执行

3.实现DisposableBean接口

实现该接口重写他的destroy方法,同样的Spring容器关闭时也会检查有无实现该接口,如有实现也会执行这里的销毁方法

下面是对于三种销毁方式的测试代码

第一端是自定义Spring容器,给容器注册钩子,这样当我们关闭Spring容器时会自动调用我们的销毁方法

1

2

3

4

5

6

public class AppStartClass {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(yuCloseSpring.class);

        annotationConfigApplicationContext.start();

        annotationConfigApplicationContext.registerShutdownHook();

    }

这一段是测试代码了,分别使用三种方式写了销毁方法

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

public class MyDisposableBean  implements DisposableBean{

    @Override

    public void destroy() throws Exception {

        System.out.println("执行DisposableBean的销毁方法");

    }

    public void test(){

        System.out.println("执行destroy-method销毁方法");

    }

    @PreDestroy

    public void testPreDestroy(){

        System.out.println("执行PreDestroy注解修饰的销毁方法");

    }

}

@Configuration

class yuCloseSpring{

    @Bean(destroyMethod = "test")

    public MyDisposableBean getMyDisposableBean(){

        return  new MyDisposableBean();

    }

}

下面是启动main方法后的执行截图,可以清晰的看到三种销毁方法都是正常执行的,且他们执行顺序是固定的,即:PreDestroy–>DisposableBean–>destroy-method。

到这里其实bean整个生命周期就算是彻底结束了。

Spring Bean 的生命周期的扩展点

Spring Bean 的生命周期的扩展点超级多,老周这里不可能全部列出来,只说核心的扩展点。这也就是为什么 Spring 的扩展性很好的原因,开了很多的口子,尽可能让某个功能高内聚松耦合,用户需要哪个功能就用哪个,而不是直接来一个大而全的东西。

3.1 Bean 自身的方法

比如构造函数、getter/setter 以及 init-method 和 destory-method 所指定的方法等,也就对应着上文说的实例化 -> 属性赋值 -> 初始化 -> 销毁四个阶段。

3.2 容器级的方法(BeanPostProcessor 一系列接口)

主要是后处理器方法,比如下图的 InstantiationAwareBeanPostProcessor、BeanPostProcessor 接口方法。这些接口的实现类是独立于 Bean 的,并且会注册到 Spring 容器中。在 Spring 容器创建任何 Bean 的时候,这些后处理器都会发生作用。

3.2.1 InstantiationAwareBeanPostProcessor 源码分析

我们翻一下源码发现 InstantiationAwareBeanPostProcessor 是继承了 BeanPostProcessor

  • InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation 调用点

 Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName)
返回值:如果返回的不为null,那么后续的Bean的创建流程【实例化、初始化afterProperties】都不会执行,而是直接使用返回的快捷Bean,此时的正常执行顺序如下:
InstantiationAwareBeanPostProcessor接口中的postProcessBeforeInstantiation,在实例化之前调用。
BeanPostProcessor接口中的postProcessAfterInitialization,在实例化之后调用。

总之,postProcessBeforeInstantiation 在 doCreateBean 之前调用,也就是在 bean 实例化之前调用的,英文源码注释解释道该方法的返回值会替换原本的 Bean 作为代理,这也是 AOP 等功能实现的关键点。

  • InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation 调用点

 boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException
正常情况下在实例化之后在执行populateBean之前调用
返回值:如果有指定的bean的时候返回false,那么后续的属性填充和属性依赖注入【populateBean】将不会执行,同时后续的postProcessPropertyValues将不会执行,但是初始化和BeanPostProcessor的仍然会执行。

 public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)
实例化之后调用,在方法applyPropertyValues【属性填充】之前
返回值:如果返回null,那么将不会进行后续的属性填充,比如依赖注入等,如果返回的pvs额外的添加了属性,那么后续会填充到该类对应的属性中。
pvs:PropertyValues对象,用于封装指定类的对象,简单来说就是PropertyValue的集合,里面相当于以key-value形式存放类的属性和值。
pds:PropertyDescriptor对象数组,PropertyDescriptor相当于存储类的属性,不过可以调用set,get方法设置和获取对应属性的值。

3.2.2 BeanPostProcessor 源码分析

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

进入初始化接口:

我们先来看

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization

  •  首先获取到所有的后置处理器 getBeanPostProcessors()
  • 在 for 循环中依次调用后置处理器的方法 processor.postProcessBeforeInitialization(result, beanName);
  • 进入 postProcessBeforeInitialization 方法

 org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization

进入 invokeAwareInterfaces(bean); 方法,当前 bean 实现了 ApplicationContextAware 接口。

  • ApplicationContextAwareProcessor#postProcessBeforeInitialization 首先判断此 bean 是不是各种的Aware,如果是它列举的那几个 Aware 就获取 Bean 工厂的权限,可以向容器中导入相关的上下文环境,目的是为了 Bean 实例能够获取到相关的上下文,如果不是它列举的几个 Aware,那就调用 invokeAwareInterfaces(bean),向容器中添加相关接口的上下文环境。

 3.3 工厂后处理器方法(BeanFactoryProcessor 一系列接口)

包括 AspectJWeavingEnabler、CustomAutowireConfigurer、ConfigurationClassPostProcessor 等。这些都是 Spring 框架中已经实现好的 BeanFactoryPostProcessor,用来实现某些特定的功能。

我们知道 Spring IoC 容器初始化的关键环节就在 org.springframework.context.support.AbstractApplicationContext#refresh 方法中 ,容器创建的主体流程都在这个方法里面,这个方法是真的重要!!!

对于工厂后处理器方法老周这里直接带你看 invokeBeanFactoryPostProcessors(beanFactory); 方法,这个方法处理的是 BeanFactoryPostProcessor 接口的 Bean。调用方法如下:

跟到最重要的方法里去,代码虽长,但逻辑中规中矩。

BeanFactoryPostProcessor:一切处理 BeanFactory 的父接口
BeanDefinitionRegistryPostProcessor:实现了 BeanFactoryPostProcessor 接口的接口

流程说明:

  • 调用 BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry(registry) 方法。参数 beanFactoryPostProcessors 传入的优先处理掉。然后获取容器注册的,对于这些 Bean 按照 PriorityOrdered 接口、Ordered、没有排序接口的实例分别进行处理。
  • 调用 BeanFactoryPostProcessor#postProcessBeanFactory(beanFactory) 方法。备注:BeanDefinitionRegistryPostProcessor 属于 BeanFactoryPostProcessor 子接口。先处理属于 BeanDefinitionRegistryPostProcessor 接口实例的 postProcessBeanFactory(beanFactory) 方法,然后获取容器注册的。对于这些 Bean 按照 PriorityOrdered 接口、Ordered、没有排序接口的实例分别进行处理。

3.4 Bean 级生命周期方法

可以理解为 Bean 类直接实现接口的方法,比如 BeanNameAware、BeanFactoryAware、ApplicationContextAware、InitializingBean、DisposableBean 等方法,这些方法只对当前 Bean 生效。

3.4.1 Aware 类型的接口

Aware 类型的接口的作用就是让我们能够拿到 Spring 容器中的一些资源。基本都能够见名知意,Aware 之前的名字就是可以拿到什么资源,例如 BeanNameAware 可以拿到 BeanName,以此类推。调用时机需要注意:所有的 Aware 方法都是在初始化阶段之前调用的。

Aware 接口众多,这里同样通过分类的方式帮助大家记忆。Aware 接口具体可以分为两组,至于为什么这么分,详见下面的源码分析。如下排列顺序同样也是 Aware 接口的执行顺序,能够见名知意的接口不再解释。

Aware Group1

  • BeanNameAware
  • BeanClassLoaderAware
  • BeanFactoryAware

Aware Group2

  • EnvironmentAware
  • EmbeddedValueResolverAware

 ​​​​​​这个知道的人可能不多,实现该接口能够获取 Spring EL 解析器,用户的自定义注解需要支持 SPEL 表达式的时候可以使用,非常方便。

ApplicationContextAware(ResourceLoaderAware/ApplicationEventPublisherAware/MessageSourceAware)

这几个接口可能让人有点懵,实际上这几个接口可以一起记,其返回值实质上都是当前ApplicationContext 对象,因为 ApplicationContext 是一个复合接口,如下:

Aware 调用时机源码分析

可以看到并不是所有的 Aware 接口都使用同样的方式调用。Bean××Aware 都是在代码中直接调用的,而 ApplicationContext 相关的 Aware 都是通过 BeanPostProcessor#postProcessBeforeInitialization() 实现的。感兴趣的可以自己看一下 ApplicationContextAwareProcessor 这个类的源码,就是判断当前创建的 Bean 是否实现了相关的 Aware 方法,如果实现了会调用回调方法将资源传递给 Bean。

BeanPostProcessor 的调用时机也能在这里体现,包围住 invokeInitMethods 方法,也就说明了在初始化阶段的前后执行。

关于 Aware 接口的执行顺序,其实只需要记住第一组在第二组执行之前就行了。

3.4.2 生命周期接口

至于剩下的两个生命周期接口就很简单了,实例化和属性赋值都是 Spring 帮助我们做的,能够自己实现的有初始化和销毁两个生命周期阶段。

  • InitializingBean 对应生命周期的初始化阶段,在上面源码的 invokeInitMethods(beanName, wrappedBean, mbd);方法中调用。

有一点需要注意,因为 Aware 方法都是执行在初始化方法之前,所以可以在初始化方法中放心大胆的使用 Aware 接口获取的资源,这也是我们自定义扩展 Spring 的常用方式。
除了实现 InitializingBean 接口之外还能通过注解或者 xml 配置的方式指定初始化方法,至于这几种定义方式的调用顺序其实没有必要记。因为这几个方法对应的都是同一个生命周期,只是实现方式不同,我们一般只采用其中一种方式。

  • DisposableBean 类似于 InitializingBean,对应生命周期的销毁阶段,以ConfigurableApplicationContext#close()方法作为入口,实现是通过循环取所有实现了 DisposableBean 接口的 Bean 然后调用其 destroy() 方法,感兴趣的可以自行跟一下源码。

常用接口说明

4.1 BeanNameAware

该接口只有一个方法 setBeanName(String name),用来获取 bean 的id 或者 name

4.2 BeanFactoryAware

该接口只有一个方法 setBeanFactory(BeanFactory beanFactory),用来获取当前环境中的 BeanFactory

4.3 ApplicationContextAware

该接口只有一个方法 setApplicationContext(ApplicationContext applicationContext),用来获取当前环境中的 ApplicationContext

4.4 InitializingBean

该接口只有一个方法 afterPropertiesSet(),在属性注入完成后调用

4.5 DisposableBean

该接口只有一个方法 destroy(),在容器销毁的时候调用,在用户指定的 destroy-method 之前调用

4.6 BeanPostProcessor

该接口有两个方法:

  • postProcessBeforeInitialization(Object bean, String beanName):在初始化之前调用此方法
  • postProcessAfterInitialization(Object bean, String beanName):在初始化之后调用此方法
  • 通过方法签名我们可以知道,我们可以通过 beanName 来筛选出我们需要进行个性化定制的 bean。

4.7 InstantiationAwareBeanPostProcessor

该类是 BeanPostProcessor 的子接口,常用的有如下三个方法:

  • postProcessBeforeInstantiation(Class beanClass, String beanName):在bean实例化之前调用
  • postProcessProperties(PropertyValues pvs, Object bean, String beanName):在bean实例化之后、设置属性前调用
  • postProcessAfterInstantiation(Class beanClass, String beanName):在bean实例化之后调用

代码演示

思路:创建一个类 UserBean ,让其实现几个特殊的接口,并分别在接口实现的构造器、接口方法中断点,观察线程调用栈,分析出 Bean 对象创建和管理关键点的触发时机。

5.1 UserBean 类

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

@Component

public class UserBean implements InitializingBean, BeanNameAware, DisposableBean, ApplicationContextAware {

    private int id;

    private String name;

    public UserBean(int id, String name) {

        this.id = id;

        this.name = name;

        System.out.println("2. 调用构造函数");

    }

    public int getId() {

        return id;

    }

    public void setId(int id) {

        this.id = id;

        System.out.println("5. 属性注入 id");

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

        System.out.println("5. 属性注入 name");

    }

    @Override

    public void setBeanName(String name) {

        System.out.println("6. 调用 BeanNameAware.setBeanName() 方法");

    }

    @Override

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

        UserBean userBean = (UserBean) applicationContext.getBean("userBean");

        System.out.println(userBean);

        System.out.println("7. 调用 BeanNameAware.setBeanName() 方法");

    }

    @Override

    public void afterPropertiesSet() throws Exception {

        System.out.println("9. 调用 InitializingBean.afterPropertiesSet() 方法");

    }

    public void myInit() {

        System.out.println("10. 调用 init-method 方法");

    }

    @Override

    public void destroy() throws Exception {

        System.out.println("12. 调用 DisposableBean.destroy() 方法");

    }

    public void myDestroy() {

        System.out.println("13. 调用 destroy-method 方法");

    }

    @Override

    public String toString() {

        return "UserBean{" +

                "id=" + id +

                ", name='" + name + '\'' +

                '}';

    }

}

5.2 InstantiationAwareBeanPostProcessor 接口实现类

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

@Component

public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

    @Override

    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {

        if ("userBean".equals(beanName)) {

            System.out.println("1. 调用 InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation() 方法");

        }

        return null;

    }

    @Override

    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {

        if ("userBean".equals(beanName)) {

            UserBean userBean = (UserBean) bean;

            System.out.println("3. 调用 InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation() 方法");

            System.out.println(userBean);

        }

        return true;

    }

    @Override

    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {

        if ("userBean".equals(beanName)) {

            System.out.println("4. 调用 InstantiationAwareBeanPostProcessor.postProcessProperties() 方法");

        }

        return null;

    }

}

5.3 BeanPostProcessor 接口实现类

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

@Component

public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

        if ("userBean".equals(beanName)) {

            System.out.println("8. 调用 BeanPostProcessor.postProcessBeforeInitialization() 方法");

        }

        return bean;

    }

    @Override

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        if ("userBean".equals(beanName)) {

            System.out.println("11. 调用 BeanPostProcessor.postProcessAfterInitialization() 方法");

        }

        return bean;

    }

}

5.4 BeanFactoryPostProcessor 接口实现类

1

2

3

4

5

6

7

@Component

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override

    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

        System.out.println("0. 调用 BeanFactoryPostProcessor.postProcessBeanFactory() 方法");

    }

}

5.5 applicationContext.xml

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

<?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

        https://www.springframework.org/schema/beans/spring-beans.xsd

">

    <bean class="com.riemann.test.MyInstantiationAwareBeanPostProcessor" />

    <bean id="userBean" class="com.riemann.test.UserBean" init-method="myInit" destroy-method="myDestroy">

        <!-- 构造函数注入 -->

        <constructor-arg index="0" type="int">

            <value>1</value>

        </constructor-arg>

        <constructor-arg index="1" type="java.lang.String">

            <value>微信公众号【老周聊架构】</value>

        </constructor-arg>

        <!-- setter方法注入 -->

        <property name="id" value="2"/>

        <property name="name" value="riemann"/>

    </bean>

    <bean class="com.riemann.test.MyBeanPostProcessor" />

    <bean class="com.riemann.test.MyBeanFactoryPostProcessor" />

     

</beans>

5.6 测试类

1

2

3

4

5

6

7

public class BeanLifeCycleTest {

    public static void main(String[] args) {

        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");

        UserBean user = (UserBean) applicationContext.getBean("userBean");

        ((AbstractApplicationContext) applicationContext).close();

    }

}

5.7 控制台结果打印

Spring创建bean的流程图

流程图一

流程图2

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/47447.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GEO数据挖掘从数据下载处理质控到差异分析全流程分析步骤指南

0. 综合的教学视频介绍 GEO数据库挖掘分析作图全流程每晚11点在线教学直播录屏回放视频&#xff1a; https://www.bilibili.com/video/BV1rm42157CT/ GEO数据从下载到各种挖掘分析全流程详解&#xff1a; https://www.bilibili.com/video/BV1nm42157ii/ 一篇今年近期发表的转…

捷配总结的SMT工厂安全防静电规则

SMT工厂须熟记的安全防静电规则&#xff01; 安全对于我们非常重要&#xff0c;特别是我们这种SMT加工厂&#xff0c;通常我们所讲的安全是指人身安全。 但这里我们须树立一个较为全面的安全常识就是在强调人身安全的同时亦必须注意设备、产品的安全。 电气&#xff1a; 怎样预…

IDEA 调试 Ja-Netfilter

首先本地需要有两款IDEA 可以是相同版本&#xff0c;也可以是不同版本。反正要有两个&#xff0c;一个用来调试代码&#xff0c;一个启动。 移除原有ja-netfiler 打开你的ja-netfiler的vmoptions目录&#xff0c;修改其中的idea.vmoptions文件。移除最后一行-javaagent ...参…

分享 .NET EF6 查询并返回树形结构数据的 2 个思路和具体实现方法

前言 树形结构是一种很常见的数据结构&#xff0c;类似于现实生活中的树的结构&#xff0c;具有根节点、父子关系和层级结构。 所谓根节点&#xff0c;就是整个树的起始节点。 节点则是树中的元素&#xff0c;每个节点可以有零个或多个子节点&#xff0c;节点按照层级排列&a…

AI智能名片S2B2C商城小程序在社群去中心化管理中的应用与价值深度探索

摘要&#xff1a;随着互联网技术的飞速发展&#xff0c;社群经济作为一种新兴的商业模式&#xff0c;正逐渐成为企业与用户之间建立深度连接、促进商业增长的重要途径。本文深入探讨了AI智能名片S2B2C商城小程序在社群去中心化管理中的应用&#xff0c;通过详细分析社群去中心化…

【DGL系列】DGLGraph.out_edges简介

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 目录 函数说明 用法示例 示例 1: 获取所有边的源节点和目标节点 示例 2: 获取特定节点的出边 示例 3: 获取所有边的边ID 示例 4: 获取所有信息&a…

中国机器视觉行业上市公司市场竞争格局分析

中国机器视觉产业上市公司汇总&#xff1a;分布在各产业链环节 机器视觉就是用机器来代替人眼做测量和判断的系统&#xff0c;机器检测相较于人工视觉检测优势明显。目前&#xff0c;我国机器视觉产业的上市公司数量较多&#xff0c;分布在各产业链环节。具体包括&#xff1a;…

LeetCode-返回链表倒数第K个节点、链表的回文结构,相交链表

一、返回链表倒数第k个节点 . - 力扣&#xff08;LeetCode&#xff09; 本体思路参展寻找中间节点的方法&#xff0c;寻找中间节点是定义快慢指针&#xff0c;快指针每次走两步&#xff0c;慢指针每次走一步&#xff0c;当快指针为空或者快指针的下一个节点是空时&#xff0c;…

4000厂商默认账号密码、默认登录凭证汇总.pdf

获取方式&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1F8ho42HTQhebKURWWVW1BQ?pwdy2u5 提取码&#xff1a;y2u5

音视频开发入门教程(2)配置FFmpeg编译 ~共210节

在上一篇博客介绍了安装&#xff0c;音视频开发入门教程&#xff08;1&#xff09;如何安装FFmpeg&#xff1f;共210节-CSDN博客 感兴趣的小伙伴&#xff0c;可以继续跟着老铁&#xff0c;一起开始音视频剪辑功能&#xff0c;&#x1f604;首先查看一下自己的电脑是几核的&…

SCSA第七天

防火墙的可靠性 因为防火墙上不仅需要同步配置信息&#xff0c;还需要同步状态信息&#xff08;会话表等&#xff09;&#xff0c;所以&#xff0c;防火墙不能 像路由器那样单纯的靠动态协议来实现切换&#xff0c;需要用到双机热备技术。 1&#xff0c;双机 --- 目前双机热…

Golang面试题整理(持续更新...)

文章目录 Golang面试题总结一、基础知识1、defer相关2、rune 类型3、context包4、Go 竞态、内存逃逸分析5、Goroutine 和线程的区别6、Go 里面并发安全的数据类型7、Go 中常用的并发模型8、Go 中安全读写共享变量方式9、Go 面向对象是如何实现的10、make 和 new 的区别11、Go 关…

破解反爬虫策略 /_guard/auto.js(二)实战

这次我们用上篇文章讲到的方法来真正破解一下反爬虫策略&#xff0c;这两个案例是两个不同的网站&#xff0c;一个用的是 /_guard/auto.js&#xff0c;另一个用的是/_guard/delay_jump.js。经过解析发现这两个网站用的反爬虫策略基本是一模一样&#xff0c;只不过在js混淆和生成…

HTML2048小游戏(最新版)

比上一篇文章的2048更好一点。 控制方法&#xff1a;WASD键&#xff08;小写&#xff09;或页面上四个按钮 效果图如下&#xff1a; 源代码在图片后面 源代码 HTML <!DOCTYPE html> <html lang"en"> <head><meta charset&…

pyspark使用 graphframes创建图的方法

1、安装graphframes的步骤 1.1 查看 spark 和 scala版本 在终端输入&#xff1a; spark-shell --version 查看spark 和scala版本 1.2 在maven库中下载对应版本的graphframes https://mvnrepository.com/artifact/graphframes/graphframes 我这里需要的是spark 2.4 scala 2.…

什么是AGI?以及AGI最新技术如何?

首先&#xff0c;AGI是Artificial General Intelligence的缩写&#xff0c;意为人工通用智能。AGI指的是一种拥有与人类相当智能水平的人工智能系统&#xff0c;能够在各种不同的任务和环境中进行智能决策和问题解决。与目前大多数人工智能系统只能在特定领域下执行特定任务不同…

Kafka Producer之ACKS应答机制

文章目录 1. 应答机制2. 等级03. 等级14. 等级all5. 设置等级6. ISR 1. 应答机制 异步发送的效率高&#xff0c;但是不安全&#xff0c;同步发送安全&#xff0c;但是效率低。 无论哪一种&#xff0c;有一个关键的步骤叫做回调&#xff0c;也就是ACKS应答机制。 其中ACKS也分…

线性表的链式存储结构————双链表(java)

线性表的链式存储结构————双链表&#xff08;java&#xff09; 文章目录 线性表的链式存储结构————双链表&#xff08;java&#xff09;双链表双链表的创建插入数据元素头插法尾插法 求链表的长度输出双链表删除双链表中的指定元素总代码运行效果用Java内部类实现双链表…

Tecnomatix Plant Simulation 这本书的翻译

这本书我准备自己翻译一下&#xff0c;一是学习这个软件&#xff0c;而是提高一下英文能力。 翻译主要是利用chatgpt 书名《Tecnomatix Plant Simulation Modeling and Programming by Means of Examples》 通过示例进行建模和编程 第二版 目录 前言 1 基础知识 1.1 介绍…

【精品资料】物业行业BI大数据解决方案(43页PPT)

引言&#xff1a;物业行业BI&#xff08;Business Intelligence&#xff0c;商业智能&#xff09;大数据解决方案是专为物业管理公司设计的一套综合性数据分析与决策支持系统。该解决方案旨在通过集成、处理、分析及可视化海量数据&#xff0c;帮助物业企业提升运营效率、优化资…