Spring Bean 生命周期

 2022-09-04
原文地址:https://cloud.tencent.com/developer/article/1990967

Spring Bean 简介

如何注册一个 Spring Bean?

通过 BeanDefinition 和外部单体对象来注册

BeanDefinition

什么是 BeanDefinition

BeanDefinition 是 Spring Framework 中定义 Bean 的配置元信息接口,包含:

  • Bean 类名
  • Bean 行为配置元素,如:作用域、自动绑定的模式、生命周期回调等
  • 其他 Bean 引用
  • 配置设置,如 Bean 属性(Properties)

l#beandefinition-元信息)BeanDefinition 元信息

BeanDefinition 元信息如下:

属性(Property) 说明
ClassBean 全类名,必须是具体类,不能用抽象类或接口
NameBean 的名称或者ID
ScopeBean 的作用域(如:singleton、prototype等)
ConstructorargumentsBean 构造器参数(用于依赖注入)
PropertiesBean 属性设置(用于依赖注入)
AutowiringmodeBean 自动绑定模式(如:通过名称byName)
LazyinitializationmodeBean 延迟初始化模式(延迟和非延迟)
InitializationmethodBean 初始化回调方法名称
DestructionmethodBean 销毁回调方法名称

BeanDefinition 构建

BeanDefinition 构建方式:

  • 通过 BeanDefinitionBuilder
  • 通过 AbstractBeanDefinition 以及派生类
    // 1.通过 BeanDefinitionBuilder 构建
    BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
    // 通过属性设置
    beanDefinitionBuilder
      .addPropertyValue("id", 1)
      .addPropertyValue("name", "小马哥");
    // 获取 BeanDefinition 实例
    BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
    // BeanDefinition 并非 Bean 终态,可以自定义修改
    
    // 2. 通过 AbstractBeanDefinition 以及派生类
    GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
    // 设置 Bean 类型
    genericBeanDefinition.setBeanClass(User.class);
    // 通过 MutablePropertyValues 批量操作属性
    MutablePropertyValues propertyValues = new MutablePropertyValues();
    //        propertyValues.addPropertyValue("id", 1);
    //        propertyValues.addPropertyValue("name", "小马哥");
    propertyValues
      .add("id", 1)
      .add("name", "小马哥");
    // 通过 set MutablePropertyValues 批量操作属性
    genericBeanDefinition.setPropertyValues(propertyValues);

命名 Spring Bean

Spring Bean 命名规则

每个 Bean 拥有一个或多个标识符(identifiers),这些标识符在 Bean 所在的容器必须是唯一的。通常,一个 Bean 仅有一个标识符,如果需要额外的,可考虑使用别名(Alias)来扩充。

在基于 XML 的配置元信息中,开发人员可用 id 或者 name 属性来规定 Bean 的标识符。通常 Bean 的标识符由字母组成,允许出现特殊字符。如果要想引入 Bean 的别名的话,可在 name 属性使用半角逗号(“,”)或分号(“;”) 来间隔。

Bean 的 id 或 name 属性并非必须制定,如果留空的话,容器会为 Bean 自动生成一个唯一的名称。Bean 的命名尽管没有限制,不过官方建议采用驼峰的方式,更符合 Java 的命名约定。

Spring Bean 命名生成器

Spring 提供了两种 Spring Bean 命名生成器:

  • DefaultBeanNameGenerator:默认通用 BeanNameGenerator 实现。
  • AnnotationBeanNameGenerator:基于注解扫描的 BeanNameGenerator 实现。
    public interface BeanNameGenerator {
       String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry);
    }

Spring Bean 别名

可能存在这样的场景,不同系统中对于同一 bean 的命名方式不一样。 为了适配,Spring 支持 <alias> 为 bean 添加别名的功能。

    <bean id="user" class="io.github.dunwu.spring.core.domain.User"
      p:name="张三" p:age="18">
    </bean>
    <alias name="sysUser" alias="superUser" />

Bean 别名(Alias)的作用:

  • 复用现有的 BeanDefinition

  • 更具有场景化的命名方法,比如:

    • <alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
    • <alias name="myApp-dataSource" alias="subsystemB-dataSource"/>

Bean 生命周期流程

202209042308215021.png

  1. Spring 对 Bean 进行实例化(相当于 new XXX())

  2. Spring 将值和引用注入进 Bean 对应的属性中

  3. 如果 Bean 实现了 BeanNameAware 接口,Spring 将 Bean 的 ID 传递给 setBeanName 方法

    • 作用是通过 Bean 的引用来获得 Bean ID,一般业务中是很少有用到 Bean 的 ID 的
  4. 如果 Bean 实现了 BeanFactoryAware 接口,Spring 将调用 setBeanDactory 方法,并把 BeanFactory 容器实例作为参数传入。

    • 作用是获取 Spring 容器,如 Bean 通过 Spring 容器发布事件等
  5. 如果 Bean 实现了 ApplicationContextAware 接口,Spring 容器将调用 setApplicationContext 方法,把应用上下文作为参数传入

    • 作用与 BeanFactory 类似都是为了获取 Spring 容器,不同的是 Spring 容器在调用 setApplicationContext 方法时会把它自己作为 setApplicationContext 的参数传入,而 Spring 容器在调用 setBeanFactory 前需要使用者自己指定(注入)setBeanFactory 里的参数 BeanFactory
  6. 如果 Bean 实现了 BeanPostProcess 接口,Spring 将调用 postProcessBeforeInitialization 方法

    • 作用是在 Bean 实例创建成功后对其进行增强处理,如对 Bean 进行修改,增加某个功能
  7. 如果 Bean 实现了 InitializingBean 接口,Spring 将调用 afterPropertiesSet 方法,作用与在配置文件中对 Bean 使用 init-method 声明初始化的作用一样,都是在 Bean 的全部属性设置成功后执行的初始化方法。

  8. 如果 Bean 实现了 BeanPostProcess 接口,Spring 将调用 postProcessAfterInitialization 方法

    • postProcessBeforeInitialization 是在 Bean 初始化前执行的,而 postProcessAfterInitialization 是在 Bean 初始化后执行的
  9. 经过以上的工作后,Bean 将一直驻留在应用上下文中给应用使用,直到应用上下文被销毁

  10. 如果 Bean 实现了 DispostbleBean 接口,Spring 将调用它的 destory 方法,作用与在配置文件中对 Bean 使用 destory-method 属性的作用一样,都是在 Bean 实例销毁前执行的方法。

Spring Bean 注册

注册 Spring Bean 实际上是将 BeanDefinition 注册到 IoC 容器中。

XML 配置元信息

Spring 的传统配置方式。在 <bean> 标签中配置元数据内容。

缺点是当 JavaBean 过多时,产生的配置文件足以让你眼花缭乱。

注解配置元信息

使用 @Bean@Component@Import 注解注册 Spring Bean。

Java API 配置元信息

  • 命名方式:BeanDefinitionRegistry#registerBeanDefinition(String,BeanDefinition)
  • 非命名方式:BeanDefinitionReaderUtils#registerWithGeneratedName(AbstractBeanDefinition,Be anDefinitionRegistry)
  • 配置类方式:AnnotatedBeanDefinitionReader#register(Class...)

[示例]

    @Import(AnnotationComponentScan.MyConfiguration.class)
    public class AnnotationComponentScan {
    
       public static void main(String[] args) {
          // 创建 BeanFactory 容器
          AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    
          // 1.注册配置类
          ctx.register(AnnotationComponentScan.class);
    
          // 2.通过 Java API 注册
          registerBeanDefinition(ctx, "zhaoliu", User.class);
    
          // 启动应用上下文
          ctx.refresh();
    
          User wangwu = (User) ctx.getBean("wangwu");
          System.out.println("wangwu info: " + wangwu);
          System.out.println("All beans of User: " + ctx.getBeansOfType(User.class));
    
          //显示关闭 ApplicationContext
          ctx.close();
       }
    
       public static void registerBeanDefinition(BeanDefinitionRegistry registry, String beanName, Class<?> beanClass) {
          BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(beanClass);
          builder.addPropertyValue("name", "赵六");
          builder.addPropertyValue("age", 31);
    
          // 注册 BeanDefinition
          registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
       }
    
       @Configuration
       public static class MyConfiguration {
    
          // 注解方式配置元信息
          @Bean(name = { "user", "wangwu" })
          public User user() {
             return new User("王五", 21);
          }
    
       }
    
    }

Spring Bean 实例化

Spring Bean 实例化方式:

  • 常规方式

    • 通过构造器(配置元信息:XML、Java 注解和 Java API)
    • 通过静态工厂方法(配置元信息:XML、Java 注解和 Java API)
    • 通过 Bean 工厂方法(配置元信息:XML、Java 注解和 Java API)
    • 通过 FactoryBean(配置元信息:XML、Java 注解和 Java API)
  • 特殊方式

    • 通过 ServiceLoaderFactoryBean(配置元信息:XML、Java 注解和 Java API )
    • 通过 AutowireCapableBeanFactory#createBean(java.lang.Class, int, boolean)
    • 通过 BeanDefinitionRegistry#registerBeanDefinition(String,BeanDefinition)

Spring Bean 初始化和销毁

Spring Bean 初始化和销毁的方式有以下几种:

  1. 使用 @PostConstruct@PreDestroy 注解分别指定相应的初始化方法和销毁方法。

  2. 实现 InitializingBean 接口的 afterPropertiesSet() 方法来编写初始化方法;实现 DisposableBean 接口的 destroy() 方法来编写销毁方法。

  3. 自定义初始化方法

    • XML 配置:<bean init-method="init" destroy="destroy" ... />
    • Java 注解:@Bean(initMethod = "init", destroyMethod = "destroy")
    • Java API:AbstractBeanDefinition#setInitMethodName(String)AbstractBeanDefinition#setDestroyMethodName(String) 分别定义初始化和销毁方法

注意:如果同时存在,执行顺序会按照序列执行。

@Bean 的 initMethod 和 destroyMethod

(1)定义 Bean 实例

    public class Pojo {
    
        public Pojo() {
            System.out.println("[Pojo 构造方法]");
        }
    
        public void init() {
            System.out.println("[Pojo 初始化方法]");
        }
    
        public void destroy() {
            System.out.println("[Pojo 销毁方法]");
        }
    
    }

(2)使用 @Bean 在配置类中注册 Bean

    @Configuration
    public class AnnotationBeanDemo {
    
        @Bean(initMethod = "init", destroyMethod = "destroy")
        public Pojo pojo() {
            return new Pojo();
        }
    
    }

说明:@Bean 注解的 initMethoddestroyMethod 属性分别用于指定 Bean 对应的初始化方法和销毁方法。

(3)测试:

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AnnotationBeanDemo.class);
        System.out.println("AnnotationBeanDemo 示例结束");
        context.close();
    }

输出

    [Pojo 构造方法]
    [Pojo 初始化方法]
    AnnotationBeanDemo 示例结束
    [Pojo 销毁方法]

InitializingBeanDisposableBean

  • InitializingBean 接口包含一个 afterPropertiesSet 方法,可以通过实现该接口,然后在这个方法中编写初始化逻辑。
  • DisposableBean接口包含一个 destory 方法,可以通过实现该接口,然后在这个方法中编写销毁逻辑。

(1)定义 Bean 实例

    public static class Pojo2 implements InitializingBean, DisposableBean {
    
        public Pojo2() {
            System.out.println("[Pojo2 构造方法]");
        }
    
        @Override
        public void afterPropertiesSet() {
            System.out.println("[Pojo2 初始化方法]");
        }
    
        @Override
        public void destroy() {
            System.out.println("[Pojo2 销毁方法]");
        }
    
    }

(2)使用 @Bean 在配置类中注册 Bean

    @Configuration
    public class AnnotationBeanDemo {
    
        @Bean
        public Pojo2 pojo2() {
            return new Pojo2();
        }
    
    }

(3)测试:

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AnnotationBeanDemo.class);
        System.out.println("AnnotationBeanDemo 示例结束");
        context.close();
    }

输出

    [Pojo2 构造方法]
    [Pojo2 初始化方法]
    AnnotationBeanDemo 示例结束
    [Pojo2 销毁方法]

@PostConstruct@PreDestroy

可以使用 @PostConstruct@PreDestroy 注解修饰方法来指定相应的初始化和销毁方法。

BeanPostProcessor

Spring 提供了一个 BeanPostProcessor 接口,提供了两个方法 postProcessBeforeInitializationpostProcessAfterInitialization。其中postProcessBeforeInitialization 在组件的初始化方法调用之前执行,postProcessAfterInitialization 在组件的初始化方法调用之后执行。它们都包含两个入参:

  1. bean:当前组件对象;
  2. beanName:当前组件在容器中的名称。

(1)定义 Pojo,如上面的例子

(2)定义 BeanPostProcessor

    public class MyBeanPostProcessor implements BeanPostProcessor {
    
        public MyBeanPostProcessor() {
            System.out.println("[BeanPostProcessor] construct");
        }
    
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("[BeanPostProcessor] postProcessBeforeInitialization");
            return bean;
        }
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("[BeanPostProcessor] postProcessAfterInitialization");
            return bean;
        }
    
    }

(3)使用 @Bean 在配置类中注册 Bean

    @Bean
    public Pojo pojo() {
        return new Pojo();
    }
    
    @Bean
    public MyBeanPostProcessor myBeanPostProcessor() {
        return new MyBeanPostProcessor();
    }

(3)测试:

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =
            new AnnotationConfigApplicationContext(MyBeanPostProcessorDemo.class);
        System.out.println("BeanPostProcessorDemo 示例结束");
        context.close();
    }

输出

    [BeanPostProcessor] construct
    [Pojo 构造方法]
    [BeanPostProcessor] postProcessBeforeInitialization
    [Pojo 初始化方法]
    [BeanPostProcessor] postProcessAfterInitialization
    BeanPostProcessorDemo 示例结束
    [Pojo 销毁方法]

Spring Bean 垃圾回收

Spring Bean 垃圾回收步骤:

  1. 关闭 Spring 容器(应用上下文)
  2. 执行 GC
  3. Spring Bean 覆盖的 finalize() 方法被回调