Spring中bean的初始化和销毁几种实现方式详解

简介: Spring中bean的初始化和销毁几种实现方式详解

Bean的生命周期 : 创建bean对象 – 属性赋值 – 初始化方法调用前的操作 – 初始化方法 – 初始化方法调用后的操作 – …-- 销毁前操作 – 销毁方法的调用。

先放一张图吧。


【1】init-method和destroy-method

bean 标签有两个重要的属性(init-method 和 destroy-method)。用它们你可以自己定制初始化和注销方法。它们也有相应的注解(@PostConstruct 和@PreDestroy)。


自定义初始化方法和销毁方法两种方式:xml配置和注解。

① xml配置

<bean id="person" 
    class="com.core.Person" scope="singleton"  
    init-method="init"  destroy-method="cleanUp"
    autowire="byName" lazy-init="true" >  
</bean> 


② 注解配置

@Scope("singleton")
   @Lazy
   @Bean(name="person",initMethod="init",destroyMethod="cleanUp",
   autowire=Autowire.BY_NAME)
   public Person person01(){
       return new Person("lisi", 20);
   }

单实例bean在容器创建完成前会进行创建并初始化,在容器销毁的时候进行销毁。多实例bean(scope=prototype)在第一次获取该bean实例时才会创建并初始化,且容器不负责该bean的销毁。


【2】InitializingBean 和DisposableBean


InitializingBean 接口

public interface InitializingBean {
  void afterPropertiesSet() throws Exception;
}


在BeanFactory设置完bean属性后执行


需要被bean实现的接口,一旦bean的属性被BeanFactory设置后需要做出反应: 如,执行自定义初始化,或者仅仅是检查是否设置了所有强制属性。


实现InitializingBean 的可替代方式为给bean指定一个自定义的init-method,例如在一个xml bean 定义中。


在bean的属性设置之后进行操作,不返回任何值但是允许抛出异常。


DisposableBean接口

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

被bean实现的接口,在销毁时释放资源,在Bean销毁的时候调用该方法。

如果销毁一个缓存的单例,一个BeanFactory 可能会调用这个销毁方法。

在容器关闭时,应用上下文会销毁所有的单例bean。

一种替代实现DisposableBean 接口的方案为指定一个自定义的destroy-method方法,例如在一个xml bean定义中。


自定义bean实现上述两个接口

@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...");
  }
}

测试结果

cat constructor...
cat...afterPropertiesSet...
容器创建完成...
四月 08, 2018 6:35:46 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext 
doClose
信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@11028347: 
startup date [Sun Apr 08 18:35:46 CST 2018]; root of context hierarchy
cat...destroy...


【3】@PostConstruct和@PreDestroy

使用JSR250规范定义的两个注解:


  • @PostConstructPostConstruct注解作用在方法上,在依赖注入完成后进行一些初始化操作。这个方法在类被放入service之前被调用,所有支持依赖项注入的类都必须支持此注解。
  • @PreDestroy:在容器销毁bean之前通知我们进行清理工作


自定义类使用上述两个注解

@Component
public class Dog implements ApplicationContextAware {
  //@Autowired
  private ApplicationContext applicationContext;
  public Dog(){
    System.out.println("dog constructor...");
  }
  //对象创建并赋值之后调用
  @PostConstruct
  public void init(){
    System.out.println("Dog....@PostConstruct...");
  }
  //容器移除对象之前
  @PreDestroy
  public void detory(){
    System.out.println("Dog....@PreDestroy...");
  }
  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    // TODO Auto-generated method stub
    this.applicationContext = applicationContext;
  }
}


测试结果如下:

dog constructor...
Dog....@PostConstruct...
容器创建完成...
四月 08, 2018 6:42:11 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext 
doClose
信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@11028347: 
startup date [Sun Apr 08 18:42:10 CST 2018]; root of context hierarchy
Dog....@PreDestroy...

【4】BeanPostProcessor-Bean后置处理器

① 什么是bean后置处理器

Bean 后置处理器允许在调用初始化方法前后对 Bean 进行额外的处理。Bean 后置处理器对 IOC 容器里的所有 Bean 实例逐一处理, 而非单一实例。其典型应用是: 检查 Bean 属性的正确性或根据特定的标准更改 Bean 的属性。


在bean初始化前后进行一些处理工作

  • postProcessBeforeInitialization:在初始化之前工作
  • postProcessAfterInitialization:在初始化之后工作


其接口源码如下:

public interface BeanPostProcessor {
  Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
  Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

自定义MyBeanPostProcessor实现该接口

/**
 * 后置处理器:初始化前后进行处理工作
 * 将后置处理器加入到容器中
 */
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
  System.out.println("BeanPostProcessor.postProcessBeforeInitialization..."+beanName+"=>"+bean);
    return bean;
  }
  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    System.out.println("BeanPostProcessor.postProcessAfterInitialization..."+beanName+"=>"+bean);
    return bean;
  }
}

② BeanPostProcessor原理

AbstractAutowireCapableBeanFactory中关于bean和BeanPostProcessor执行次序由上到下

//给bean进行属性赋值
populateBean(beanName, mbd, instanceWrapper);
//然后调用initializeBean方法
Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd)
{
  applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
  //执行自定义初始化
  invokeInitMethods(beanName, wrappedBean, mbd);
  applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
//给be

AbstractAutowireCapableBeanFactory.initializeBean源码如下:

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
  if (System.getSecurityManager() != null) {
    AccessController.doPrivileged(new PrivilegedAction<Object>() {
      @Override
      public Object run() {
        invokeAwareMethods(beanName, bean);
        return null;
      }
    }, getAccessControlContext());
  }
  else {
  //调用意识/通知方法
    invokeAwareMethods(beanName, bean);
  }
  Object wrappedBean = bean;
  if (mbd == null || !mbd.isSynthetic()) {
  //调用bean后置处理器的前置方法
    wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
  }
  //调用初始化方法
  try {
    invokeInitMethods(beanName, wrappedBean, mbd);
  }
  catch (Throwable ex) {
    throw new BeanCreationException(
        (mbd != null ? mbd.getResourceDescription() : null),
        beanName, "Invocation of init method failed", ex);
  }
  if (mbd == null || !mbd.isSynthetic()) {
  //  //调用bean后置处理器的后置方法
    wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
  }
  return wrappedBean;
}
protec

AbstractAutowireCapableBeanFactory.invokeInitMethods方法源码如下:

protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
    throws Throwable {
  boolean isInitializingBean = (bean instanceof InitializingBean);
  if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
    if (logger.isDebugEnabled()) {
      logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
    }
    //调用InitializingBean.afterPropertiesSet
    if (System.getSecurityManager() != null) {
      try {
        AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
          @Override
          public Object run() throws Exception {
            ((InitializingBean) bean).afterPropertiesSet();
            return null;
          }
        }, getAccessControlContext());
      }
      catch (PrivilegedActionException pae) {
        throw pae.getException();
      }
    }
    else {
      ((InitializingBean) bean).afterPropertiesSet();
    }
  }
//调用自定义初始化方法
  if (mbd != null) {
    String initMethodName = mbd.getInitMethodName();
    if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
        !mbd.isExternallyManagedInitMethod(initMethodName)) {
      invokeCustomInitMethod(beanName, bean, mbd);
    }
  }
}

【5】Spring底层使用的BeanPostProcessor

Spring框架底层存在大量BeanPostProcessor,如下图:

示例一 :BeanValidationPostProcessor是处理bean校验


其Javadoc如下:

//为spring管理的bean,校验JSR-303注解约束。如果校验不通过则在调用bean的init method前抛出初始化异常
public class BeanValidationPostProcessor implements BeanPostProcessor, InitializingBean {
  private Validator validator;
  private boolean afterInitialization = false;
  //...
}

示例二:ApplicationContextAwareProcessor帮助获取容器上下文

其Javadoc如下:

// 传递ApplicationContext 
class ApplicationContextAwareProcessor implements BeanPostProcessor {
  private final ConfigurableApplicationContext applicationContext;
  private final StringValueResolver embeddedValueResolver;
  //...
}

如【3】中的dog类为例,其debug示意图如下:


【6】初始化和销毁方式测试

① 如果一个bean 综合应用下面六种种方式,执行顺序会怎样呢


Bean类如下:

public class Person implements InitializingBean,DisposableBean {
    private String name;
    private  Integer age=1;
    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
        System.out.println("Person(String name, Integer age) constructor"+this);
    }
    public Person() {
        super();
        System.out.println("Person() constructor"+age);
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
  // 自定义init方法
    public void init(){
        System.out.println("-----Person.init()-----"+this);
    }
    // 自定义销毁方法
    public void cleanUp(){
        System.out.println("-----Person.cleanUp()-----"+this);
    }
  // InitializingBean的实现方法
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("-----InitializingBean.afterPropertiesSet()-----"+this);
    }
  //DisposableBean 的实现方法
    @Override
    public void destroy() throws Exception {
        System.out.println("-----DisposableBean.destroy()-----"+this);
    }
    //对象创建并赋值之后调用
    @PostConstruct
    public void init2(){
        System.out.println("-----@PostConstruct-----"+this);
    }
    //容器移除对象之前
    @PreDestroy
    public void destory2(){
        System.out.println("-----@PreDestroy-----"+this);
    }
}


配置类如下:

@Scope("singleton")
@Bean(name="person",initMethod="init",destroyMethod="cleanUp",
        autowire= Autowire.BY_NAME)
public Person person01(){
    return new Person("lisi", 20);
}

测试结果如下:

// 创建并初始化
Person(String name, Integer age) constructorPerson{name='lisi', age=20}
-----@PostConstruct-----Person{name='lisi', age=20}
-----InitializingBean.afterPropertiesSet()-----Person{name='lisi', age=20}
-----Person.init()-----Person{name='lisi', age=20}
//容器将要销毁
-----@PreDestroy-----Person{name='lisi', age=20}
-----DisposableBean.destroy()-----Person{name='lisi', age=20}
-----Person.cleanUp()-----Person{name='lisi', age=20}

即,最先使用bean的构造器为bean属性赋值,接着JSR250规范定义的两个注解,其次是InitializingBean和DisposableBean接口,最后才是我们自定义的初始化方法和销毁方法。注意,这里还没有引入BeanPostProcessor。


② 在①的基础上添加BeanPostProcessor

实例化bean并进行初始化

//调用构造方法
Person(String name, Integer age) constructorPerson{name='lisi', age=20}
//bean初始化前
BeanPostProcessor.postProcessBeforeInitialization...person=>Person{name='lisi', age=20}
//初始化操作
-----@PostConstruct-----Person{name='lisi', age=20}
-----InitializingBean.afterPropertiesSet()-----Person{name='lisi', age=20}
-----Person.init()-----Person{name='lisi', age=20}
//bean初始化后操作
BeanPostProcessor.postProcessAfterInitialization...person=>Person{name='lisi', age=20}


过程如下:类构造函数-->BeanPostProcessor-->@PostConstruct-->InitializingBean-->init()-->BeanPostProcessor


销毁bean

-----@PreDestroy-----Person{name='lisi', age=20}
-----DisposableBean.destroy()-----Person{name='lisi', age=20}
-----Person.cleanUp()-----Person{name='lisi', age=20}


完整图示如下(同颜色的说明相对应):

在调用bean的构造函数时会根据入参为bean属性赋值,如果入参为空则会给bean属性赋予默认值,引用类型为null,基本类型比如int为0。



【7】 @Autowired注解的值何时放入?

public Person(String name, Integer age) {
    this.name = name;
    this.age = age;
    System.out.println("Person(String name, Integer age) constructor"+this);
    System.out.println(" @Autowired adviceService:"+adviceService);
}
@Override
public void setBeanName(String name) {
    System.out.println(" @Autowired adviceService:"+adviceService);
    System.out.println("BeanNameAware--setBeanName...."+name);
}


测试结果:

// 如下是构造函数中打印
Person(String name, Integer age) constructorPerson{name='lisi', age=20}
@Autowired adviceService:null
// 如下 是setBeanName方法中打印
@Autowired adviceService:com.recommend.service.impl.SysAdviceServiceImpl@5a739e57
BeanNameAware--setBeanName....person


那么也就是说,在类的构造函数调用、setXXX属性后,开始执行@Autowired依赖注入。这个过程发生在setBeanName时机之前。


那么具体什么时候哪个类完成的 @Autowired注解注入依赖呢?


在类被实例化后由BeanPostProcessor完成的,哪个BeanPostProcessor?


具体是由AutowiredAnnotationBeanPostProcessor 完成的:


【8】如果bean实现了XXXAware接口呢?

这里比如Person实现了BeanNameAware、BeanFactoryAware以及ApplicationContextAware接口。


如果这个 Bean 已经实现了 BeanNameAware 接口,会调用它实现的setBeanName(String) 方法,此处传递的就是 Spring 配置文件中 Bean 的 id 值。


如果这个 Bean 已经实现了 BeanFactoryAware 接口,会调用它实现的 setBeanFactory,

setBeanFactory(BeanFactory)传递的是 Spring 工厂自身(可以用这个方式来获取其它 Bean,只需在 Spring 配置文件中配置一个普通的 Bean 就可以)。


如果这个 Bean 已经实现了 ApplicationContextAware 接口,会调用

setApplicationContext(ApplicationContext)方法,传入 Spring 上下文(同样这个方式也可以实现步骤 setBeanFactory 的内容,但比 setBeanFactory 更好,因为ApplicationContext 是 BeanFactory 的子接口,有更多的实现方法)

public class Person implements InitializingBean, DisposableBean, BeanNameAware, BeanFactoryAware, ApplicationContextAware {
    private String name;
    private  Integer age=1;
    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
        System.out.println("Person(String name, Integer age) constructor"+this);
    }
    public Person() {
        super();
        System.out.println("Person() constructor"+age);
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
        System.out.println("setName方法..."+name);
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    // 自定义init方法
    public void init(){
        System.out.println("-----Person.init()-----"+this);
    }
    // 自定义销毁方法
    public void cleanUp(){
        System.out.println("-----Person.cleanUp()-----"+this);
    }
    // InitializingBean的实现方法
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("-----InitializingBean.afterPropertiesSet()-----"+this);
    }
    //DisposableBean 的实现方法
    @Override
    public void destroy() throws Exception {
        System.out.println("-----DisposableBean.destroy()-----"+this);
    }
    //对象创建并赋值之后调用
    @PostConstruct
    public void init2(){
        System.out.println("-----@PostConstruct-----"+this);
    }
    //容器移除对象之前
    @PreDestroy
    public void destory2(){
        System.out.println("-----@PreDestroy-----"+this);
    }
    @Override
    public void setBeanName(String name) {
        System.out.println("BeanNameAware--setBeanName...."+name);
    }
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("BeanFactoryAware.setBeanFactory...."+beanFactory);
    }
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("ApplicationContextAware.setApplicationContext...."+applicationContext);
    }
}

同样在配置类中配置person bean如下

@Configuration
public class MyConfiguration {
    @Scope("singleton")
    @Bean(name="person",initMethod="init",destroyMethod="cleanUp",
            autowire= Autowire.BY_NAME)
    public Person person01(){
        return new Person("lisi", 20);
    }
}





目录
相关文章
|
25天前
|
XML Java 数据格式
Spring从入门到入土(bean的一些子标签及注解的使用)
本文详细介绍了Spring框架中Bean的创建和使用,包括使用XML配置文件中的标签和注解来创建和管理Bean,以及如何通过构造器、Setter方法和属性注入来配置Bean。
58 9
Spring从入门到入土(bean的一些子标签及注解的使用)
|
15天前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。
|
2月前
|
XML Java 数据格式
spring复习02,xml配置管理bean
详细讲解了Spring框架中基于XML配置文件管理bean的各种方式,包括获取bean、依赖注入、特殊值处理、属性赋值、集合类型处理、p命名空间、bean作用域及生命周期和自动装配。
spring复习02,xml配置管理bean
|
25天前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细解析Spring Bean的生命周期及其核心概念,并深入源码分析。Spring Bean是Spring框架的核心,由容器管理其生命周期。从实例化到销毁,共经历十个阶段,包括属性赋值、接口回调、初始化及销毁等。通过剖析`BeanFactory`、`ApplicationContext`等关键接口与类,帮助你深入了解Spring Bean的管理机制。希望本文能助你更好地掌握Spring Bean生命周期。
61 1
|
27天前
|
Java Spring
获取spring工厂中bean对象的两种方式
获取spring工厂中bean对象的两种方式
23 1
|
28天前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细介绍了Spring框架中的核心概念——Spring Bean的生命周期,包括实例化、属性赋值、接口回调、初始化、使用及销毁等10个阶段,并深入剖析了相关源码,如`BeanFactory`、`DefaultListableBeanFactory`和`BeanPostProcessor`等关键类与接口。通过理解这些核心组件,读者可以更好地掌握Spring Bean的管理和控制机制。
68 1
|
2月前
|
XML Java 数据格式
spring复习03,注解配置管理bean
Spring框架中使用注解配置管理bean的方法,包括常用注解的标识组件、扫描组件、基于注解的自动装配以及使用注解后的注意事项,并提供了一个基于注解自动装配的完整示例。
spring复习03,注解配置管理bean
|
2月前
|
SQL 监控 druid
springboot-druid数据源的配置方式及配置后台监控-自定义和导入stater(推荐-简单方便使用)两种方式配置druid数据源
这篇文章介绍了如何在Spring Boot项目中配置和监控Druid数据源,包括自定义配置和使用Spring Boot Starter两种方法。
|
22天前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
118 2
|
3月前
|
缓存 Java Maven
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决