深入学习Spring Bean生命周期(Bean的初始化销毁以及BeanPostProcessor)

导读:本篇文章讲解 深入学习Spring Bean生命周期(Bean的初始化销毁以及BeanPostProcessor),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

所谓Spring Bean的生命周期指的是Bean从创建到初始化再到销毁的过程,这个过程由IOC容器管理。

1. Bean的生命周期

传统的Java应用,Bean的生命周期很简单。使用Java关键字new进行Bean的实例化,然后该Bean就可以使用了。一旦该Bean不再被使用,则有GC选择回收。

相比之下,在Spring容器中,Bean的生命周期要细腻的多,大致过程如下图所示:

image-20220501214044802

1.Spring对Bean进行实例化;

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

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

4.如果Bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()接口方法,将BeanFactory容器实例传入;

5.如果Bean实现了ApplicationContextAware接口,Spring将调用setApplicationContext()接口方法,将应用上下文的引用传入;

6.如果Bean实现了BeanPostProcessor接口,Spring将调用它们的postProcessBeforeInitialization()接口方法;

7.如果Bean实现了InitializingBean接口,Spring将调用它们的afterPropertiesSet()接口方法。类似的,如果Bean使用init-method声明了初始化方法,该方法也会被调用。

8.如果Bean实现了BeanPostProcessor接口,Spring将调用它们的postProcessAfterInitialization()方法;

9.此时此刻,Bean已经准备就绪,可以被使用了。它们将一直驻留在Spring容器中,直到容器被销毁;

10.如果Bean实现了DisposableBean接口,Spring将调用它的destroy()接口方法。同样,如果Bean使用destroy-method声明了销毁方法,该方法也会被调用;

2. Bean的初始化和销毁

在整个生命周期过程中,我们可以自定义Bean的初始化和销毁钩子函数,当Bean的生命周期到达相应的阶段的时候,Spring会调用我们自定义的Bean的初始化和销毁方法。自定义Bean初始化和销毁方法有多种方式,下面逐一介绍。

2.1 @Bean

深入理解Spring组件注册 中介绍了可以在配置类中通过@Bean注解来注册Bean,我们也可以通过它来指定Bean的初始化和方法。

我们新建一个Spring Boot项目,然后创建一个UserBean类来进行相关的演示:

public class UserBean {

    public UserBean() {
        System.out.println("调用无参构造器创建UserBean");
    }

    public void init() {
        System.out.println("初始化UserBean");
    }

    public void destory() {
        System.out.println("销毁UserBean");
    }

}

然后在配置类里注册该组件,并指定初始化和销毁方法:

@Configuration
public class WebConfig {

    @Bean(initMethod = "init", destroyMethod = "destory")
    public UserBean userBean() {
        return new UserBean();
    }
}

其中initMethod = "init"destroyMethod = "destory"与User类里的initdestory方法相对应。

在Spring Boot入口类中测试:

// 返回 IOC 容器,使用注解配置,传入配置类
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class);
UserBean bean = context.getBean(UserBean.class);
// 关闭 IOC 容器
context.close();

启动项目,观察控制台输出:

image-20220501195712979

从上面的输出我们看出在IOC容器启动的时候会创建对象,然后调用的时候也会先调用对象的无参构造器创建对象,然后调用初始化方法,在容器关闭的时候调用销毁方法。

上面的情况是对于单例而言的,如果组件是多例模式又是什么情况呢?我们把上面的组件注册配置改为多例,然后再次启动项目,观察控制台输出:

image-20220501200020866

控制台的输出和我们之前讨论的一致,即在多例模式下,IOC容器启动的时候并不会去创建对象,而是在每次获取的时候才会去调用方法创建对象,创建完对象后再调用初始化方法。但在容器关闭后,Spring并没有调用相应的销毁方法,这是因为在多例模式下,容器不会管理这个组件(只负责在你需要的时候创建这个组件),所以容器在关闭的时候并不会调用相应的销毁方法。

2.2 InitializingBean&DisposableBean

除了上面这种方式指定初始化和销毁方法外,Spring还为我们提供了和初始化,销毁相对应的接口:

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

新建一个类,名称为Bird,然后实现这两个接口:

public class Bird implements InitializingBean, DisposableBean {
    public Bird() {
        System.out.println("调用无参构造器创建Bird");
    }

    @Override
    public void destroy() {
        System.out.println("销毁Bird");
    }

    @Override
    public void afterPropertiesSet() {
        System.out.println("初始化Bird");
    }
}

在配置类中注册这个组件:

@Bean
public Bird bird() {
    return new Bird();
}

启动项目,观察控制台输出:

image-20220501203920541

2.3 @PostConstruct&@PreDestroy

除了上面两种指定初始化和销毁方法的方式外,我们还可以使用@PostConstruct@PreDestroy注解修饰方法来指定相应的初始化和销毁方法。

新建一个类,名称为Fish:

public class Fish {
    public Fish() {
        System.out.println("调用无参构造器创建Fish");
    }

    @PostConstruct
    public void init() {
        System.out.println("初始化Fish");
    }

    @PreDestroy
    public void destory() {
        System.out.println("销毁Fish");
    }
}

在配置类中注册这个组件:

@Bean
public Fish fish(){
    return new Fish();
}

启动项目,观察控制台输出:

image-20220501204239468

效果和上面两种方式一致。

这两个注解并非Spring提供,而是JSR250规范提供。

3. BeanPostProcessor

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

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

两个方法都返回一个Object类型,我们可以直接返回当前组件对象,或者包装后返回。

我们来定义一个BeanPostProcessor接口的实现类MyBeanPostProcessor

public class MyBeanPostProcessor implements BeanPostProcessor {
    
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName + " 初始化之前调用");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName + " 初始化之后调用");
        return bean;
    }
}

在配置类中注册该组件:

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

再次启动项目,观察控制台输出:

image-20220501204619866

我们使用了这个接口,是针对所有的Bean实例的,只要一个Bean初始化都会调用对应的方法,所以叫做Bean后置通知处理器。

使用这个接口我们可以做很多的操作,比如我们之前手写RPC框架的时候,将带有注解的类注册到zookeeper上。

4. 项目地址

spring案例地址

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/16700.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!