Spring学习笔记:Bean初始化

本文是自己学习的一个总结

这里写目录标题

  • 1、bean初始化简介
    • 1.1、bean的初始化发生在什么阶段,做了什么事
  • 2、Bean初始化的回调函数
    • 2.1、基于@PostConstruct,构造后回调函数
      • 2.1.1、使用@PostConstruct
      • 2.1.2、同一个类中使用多个@PostConstruct
      • 2.1.3、注解生效范围
    • 2.2、实现InitInitializingBean覆写afterPropertiesSet
      • 2.2.1、afterPropertiesSet的生效范围
    • 2.3、基于@Bean的initMethod属性,初始化后回调函数
      • 2.3.1、initMethod的生效范围
    • 2.4、@PostConstruct,afterPropertiesSet和initMethod的执行顺序
  • 3、bean的延迟初始化
    • 3.1、bean设置延迟初始化的方式
    • 3.2、延迟初始化的时机和非延迟初始化的时机


1、bean初始化简介

1.1、bean的初始化发生在什么阶段,做了什么事

当我们从xml文件或者注解中生成容器时,XML文件或者注解中描述的bean就完成了初始化。

所谓初始化,就是bean的元信息加载进容器,说具体也就是bean信息由xml文件或者注解中读取出来,加载为BeanDefinition,再通过BeanDefinitionRegistry将这些BeanDefiniton注册到容器中的过程。

其中,这个初始化过程又可以分为构造->属性填充->初始化完成,这三个阶段又分别对应着接下来的三类回调函数。


2、Bean初始化的回调函数

2.1、基于@PostConstruct,构造后回调函数

2.1.1、使用@PostConstruct

@PostConstruct,从名字上看就能知道,是bean完成初始化之后的回调注解。该注解的使用方式是在被定义成Bean的类A中实现一个方法,并用@PostContruct标注这个方法,那么这个方法就是类A作为Bean完成初始化之后的回调方法。类A作为bean在容器中初始化之后,会调用被@PostContruct标注的方法。

我们看看例子。
DefaultUserFactory的实现如下,这是我们要注册为Bean的类。

@Component
public class DefaultUserFactory {
    @PostConstruct
    public void init() {
        System.out.println("@PostConstruct:UserFactory 初始化");
    }
}

之后在和DefaultUserFactory同一个包下定义扫描类。

@ComponentScan
public class ConfigScan {}

最后根据扫描类生成容器,我们看系统对DefaultUserFactory完成初始化后会不会调用init()函数。

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigScan.class);

最后打印结果如下,回调函数成功调用。
在这里插入图片描述

2.1.2、同一个类中使用多个@PostConstruct

@PostConstruct在类中使用标注方法,当这个类被初始化成bean之后就会调用@PostConstruct标注的方法。

其中,@PostConstruct可以在类中标注多个方法,并且类被初始化成bean之后,所有被@PostConstruct标注的方法都会被回调,但是调用的顺序不能保证,并不是按定义顺序调用的,系统似乎有自己的一套规则。

2.1.3、注解生效范围

@PostConstruct在类A 中使用,那只有类A是通过注解的方式初始化成bean时,@PostConstruct才会生效。

但是如果类A是通过XML初始化成bean,那@PostConstruct就不会起作用。


2.2、实现InitInitializingBean覆写afterPropertiesSet

若类A要被注册为bean,那可令类A实现InitInitializingBean接口,覆写其中的afterPropertiesSet方法。这样A作为bean在初始化阶段中,属性填充以后,会回调覆写的afterPropertiesSet方法。

基于上面的代码,我们在DefaultUserFactory中加入afterPropertiesSet相关的代码

@Component
public class DefaultUserFactory implements UserFactory, InitializingBean {
    @PostConstruct
    public void init() {
        System.out.println("@PostConstruct:UserFactory 初始化");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet:DefaultUserFactory初始化");
    }
}

扫描类和生成容器的代码不变。

public class ConfigScan {}
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigScan.class);

最后输出结果如下
在这里插入图片描述

2.2.1、afterPropertiesSet的生效范围

实现InitInitializingBean覆写afterPropertiesSet,这个方法与@PostConstruct不同,无论是通过注解还是通过xml,afterPropertiesSet都可以生效。


2.3、基于@Bean的initMethod属性,初始化后回调函数

@Bean中有个属性是initMethod,他是用来将类中的某个方法指定为初始化函数。我们看看例子。

接着上面的代码,我们在DefaultUserFactory中加入@Bean相关的代码

@Component
public class DefaultUserFactory implements UserFactory{
    @PostConstruct
    public void init() {
        System.out.println("@PostConstruct:UserFactory 初始化");
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet:DefaultUserFactory初始化");
    }
    @Bean(initMethod = "initFactory")
    public DefaultUserFactory getDefaultUserFactory() {
        return new DefaultUserFactory();
    }
    public void initFactory() {
        System.out.println("@Bean.initMethod:DefaultUserFactory初始化");
    }
}

可以预测,最后生成的容器中有两个类型为DefaultUserFactory的Bean。一个是基于@Component生成的bean,这个bean只设置了@PostConstruct和afterProperties的回调函数;另一个是基于@Bean生成的bean,这个类不仅有@PostConstruct和afterProperties的回调函数,还有initMethod的回调函数。

扫描类和生成容器的代码不变。

public class ConfigScan {}
ApplicationContext applicationContext = new 
AnnotationConfigApplicationContext(ConfigScan.class);

最后输出结果如下

在这里插入图片描述

2.3.1、initMethod的生效范围

因为这也是基于注解实现的,所以只有容器是通过注解生成时initMethod才有效,通过xml生成容器时initMethod会无效。

2.4、@PostConstruct,afterPropertiesSet和initMethod的执行顺序

文章开头说过,bean的初始化又分为构造->属性填充->初始化,而以上三种方式就对应着这三个阶段。

@PostConstruct在构造结束后会被调用,afterPropertiesSet在属性填充后会被调用,initMethod在初始化完成后会被调用。





3、bean的延迟初始化

3.1、bean设置延迟初始化的方式

一般来说说只有两种,一种是xml中设置,另一种是注解中设置。

  • @Lazy(true)

3.2、延迟初始化的时机和非延迟初始化的时机

延迟初始化是在容器启动之后,需要实例化bean时才会初始化这个bean,而非延迟初始化则是在容器启动是就完成初始化。

我们在上面代码的基础上给DefaultUserFactory加上@Lazy注解

@Lazy
public class DefaultUserFactory implements InitializingBean{···}

同时在启动容器的语句之后加上一段输出语句表示容器启动了,然后实例化defaultUserFactory。

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigScan.class);
System.out.println("容器启动了");
applicationContext.getBean("defaultUserFactory");

最后输出语句是
在这里插入图片描述
可以看出,defaultUserFactory是在容器启动后,需要实施化时才去初始化。

如果DefaultUserFactory没有@Lazy注解,输出语句则是下图所示,初始化在容器启动时。

在这里插入图片描述

你可能感兴趣的:(Spring)