使用spring容器在初始化Bean时前和后的操作

目录
  • spring容器初始化Bean操作
    • @PostConstruct和@PreDestroy注解
    • 在XML中定义init-method和destory-method方法
    • Bean实现InitializingBean和DisposableBean接口
  • Spring bean 初始化顺序
    • 1、概述
    • 2、InitializingBean vs init-method
    • 3、@PostConstruct
    • 4、小结一下吧

spring容器初始化Bean操作

在某些情况下,Spring容器在初始化Bean的时候,希望在初始化bean前和销毁bean前进行一些资源的加载和释放的操作。可以通过一下三种方式完成。

  • Bean的方法加上@PostConstruct和@PreDestroy注解
  • 在xml中定义init-method和destory-method方法
  • Bean实现InitializingBean和DisposableBean接口

@PostConstruct和@PreDestroy注解

JavaBean代码

@Component
public class PersonService {
    private String message;
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    @PostConstruct
    public void init() {
        System.out.println("PersonService.class init method ...");
    }
    @PreDestroy
    public void cleanUp() {
        System.out.println("PersonService.class cleanUp method ...");
    }
}

spring配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.2.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd">
    <!-- spring扫描的路径 -->
    <context:component-scan base-package="spring.zhujie" />
</beans>

测试代码和结果

测试代码

public static void main(String[] args) {
     AbstractApplicationContext context = new ClassPathXmlApplicationContext("spring-zhujie.xml");
     context.registerShutdownHook();
}

运行结果

PersonService.class init method ...

PersonService.class cleanUp method ...

在XML中定义init-method和destory-method方法

JavaBean代码

public class PersonService {
    private String message;
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    public void init() {
        System.out.println("PersonService.class init method ...");
    }
    public void cleanUp() {
        System.out.println("PersonService.class cleanUp method ...");
    }
}

spring配置文件

<bean class="spring.zhujie.PersonService" init-method="init" destroy-method="cleanUp"/>

测试代码和结果

测试代码

public static void main(String[] args) {
        AbstractApplicationContext context = new ClassPathXmlApplicationContext("spring-xml.xml");
        context.registerShutdownHook();
}

运行结果

PersonService.class init method ...

六月 23, 2017 9:42:06 下午 org.springframework.context.support.ClassPathXmlApplicationContext doClose

信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@7a94c5e7: startup date [Fri Jun 23 21:42:06 CST 2017]; root of context hierarchy

PersonService.class cleanUp method ...

Bean实现InitializingBean和DisposableBean接口

JavaBean代码

public class PersonService implements InitializingBean, DisposableBean {
    private String message;
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("PersonService.class init method ...");
    }
    @Override
    public void destroy() throws Exception {
        System.out.println("PersonService.class cleanUp method ...");
    }
}

spring配置文件

<bean id="personService" class="spring.zhujie.PersonService" />

测试代码和结果

测试代码

public static void main(String[] args) {
        AbstractApplicationContext context = new ClassPathXmlApplicationContext("spring-interface.xml");
        context.registerShutdownHook();
    }

运行结果

PersonService.class init method ...

PersonService.class cleanUp method ...

Spring bean 初始化顺序

InitializingBean, init-method 和 PostConstruct

1、概述

从接口的名字上不难发现,InitializingBean 的作用就是在 bean 初始化后执行定制化的操作。

Spring 容器中的 Bean 是有生命周期的,Spring 允许在 Bean 在初始化完成后以及 Bean 销毁前执行特定的操作,常用的设定方式有以下三种:

通过实现 InitializingBean/DisposableBean 接口来定制初始化之后/销毁之前的操作方法;

通过 <bean> 元素的 init-method/destroy-method 属性指定初始化之后 /销毁之前调用的操作方法;

在指定方法上加上@PostConstruct 或@PreDestroy注解来制定该方法是在初始化之后还是销毁之前调用。

2、InitializingBean vs init-method

接口定义如下:

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

接口只有一个方法afterPropertiesSet,

此方法的调用入口是负责加载 spring bean 的AbstractAutowireCapableBeanFactory,源码如下:

protected void invokeInitMethods(String beanName, Object bean,
   RootBeanDefinition mbd) throws Throwable {
  boolean isInitializingBean = bean instanceof InitializingBean;
  if ((isInitializingBean)
    && (((mbd == null) || (!(mbd
      .isExternallyManagedInitMethod("afterPropertiesSet")))))) {
   if (this.logger.isDebugEnabled()) {
    this.logger
      .debug("Invoking afterPropertiesSet() on bean with name '"
        + beanName + "'");
   }
   //先调用afterPropertiesSet()进行初始化
   if (System.getSecurityManager() != null) {
    try {
     AccessController.doPrivileged(
       new PrivilegedExceptionAction(bean) {
        public Object run() throws Exception {
         ((InitializingBean) this.val$bean)
           .afterPropertiesSet();
         return null;
        }
       }, getAccessControlContext());
    } catch (PrivilegedActionException pae) {
     throw pae.getException();
    }
   } else {
    ((InitializingBean) bean).afterPropertiesSet();
   }
  }

  //然后调用InitMethod()进行初始化
  if (mbd != null) {
   String initMethodName = mbd.getInitMethodName();
   if ((initMethodName == null)
     || ((isInitializingBean) && ("afterPropertiesSet"
       .equals(initMethodName)))
     || (mbd.isExternallyManagedInitMethod(initMethodName)))
    return;
   invokeCustomInitMethod(beanName, bean, mbd);
  }
 }

从这段源码可以得出以下结论:

spring为bean提供了两种初始化bean的方式,实现InitializingBean接口,实现afterPropertiesSet方法,或者在配置文件中通过init-method指定,两种方式可以同时使用

实现InitializingBean接口是直接调用afterPropertiesSet方法,比通过反射调用init-method指定的方法效率相对来说要高点。但是init-method方式消除了对spring的依赖

先调用afterPropertiesSet,再执行 init-method 方法,如果调用afterPropertiesSet方法时出错,则不调用init-method指定的方法

3、@PostConstruct

通过 debug 和调用栈找到类InitDestroyAnnotationBeanPostProcessor, 其中的核心方法,即 @PostConstruct 方法调用的入口:

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
        try {
            metadata.invokeInitMethods(bean, beanName);
        }
        catch (InvocationTargetException ex) {
            throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
        }
        catch (Throwable ex) {
            throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
        }
        return bean;
    }

从命名上,我们就可以得到某些信息——这是一个BeanPostProcessor。BeanPostProcessor的postProcessBeforeInitialization是在Bean生命周期中afterPropertiesSet和init-method之前被调用的。另外通过跟踪,@PostConstruct方法的调用方式也是通过反射机制。

4、小结一下吧

spring bean的初始化执行顺序:构造方法 --> @PostConstruct注解的方法 --> afterPropertiesSet方法 --> init-method指定的方法。具体可以参考例子

afterPropertiesSet通过接口实现方式调用(效率上高一点),@PostConstruct和init-method都是通过反射机制调用

同理,bean销毁过程的顺序为:@PreDestroy > DisposableBean > destroy-method

不再展开,看源码就好

测试代码如下:

@Slf4j
public class InitSequenceBean implements InitializingBean {
    public InitSequenceBean() {
        log.info("InitSequenceBean: construct");
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        log.info("InitSequenceBean: afterPropertiesSet");
    }
    @PostConstruct
    public void postConstruct() {
        log.info("InitSequenceBean: postConstruct");
    }
    public void initMethod() {
        log.info("InitSequenceBean: initMethod");
    }
}
@Configuration
public class SystemConfig {
    @Bean(initMethod = "initMethod", name = "initSequenceBean")
    public InitSequenceBean initSequenceBean() {
        return new InitSequenceBean();
    }
}
@Slf4j
public class InitSequenceBeanTest extends ApplicationTests {
    @Autowired
    private InitSequenceBean initSequenceBean;
    @Test
    public void initSequenceBeanTest() {
        log.info("Finish: {}", initSequenceBean.toString());
    }
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • 如何正确控制springboot中bean的加载顺序小结篇

    1.为什么需要控制加载顺序 springboot遵从约定大于配置的原则,极大程度的解决了配置繁琐的问题.在此基础上,又提供了spi机制,用spring.factories可以完成一个小组件的自动装配功能. 在一般业务场景,可能你不大关心一个bean是如何被注册进spring容器的.只需要把需要注册进容器的bean声明为@Component即可,spring会自动扫描到这个Bean完成初始化并加载到spring上下文容器. 而当你在项目启动时需要提前做一个业务的初始化工作时,或者你正在开发某个中间

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

    Bean的生命周期 : 创建bean对象 – 属性赋值 – 初始化方法调用前的操作 – 初始化方法 – 初始化方法调用后的操作 – --- 销毁前操作 – 销毁方法的调用. [1]init-method和destroy-method 自定义初始化方法和销毁方法两种方式:xml配置和注解. ① xml配置 <bean id="person" class="com.core.Person" scope="singleton" init-meth

  • 浅谈spring容器中bean的初始化

    当我们在spring容器中添加一个bean时,如果没有指明它的scope属性,则默认是singleton,也就是单例的. 例如先声明一个bean: public class People { private String name; private String sex; public String getName() { return name; } public void setName(String name) { this.name = name; } public String get

  • SpringBoot中的Bean的初始化与销毁顺序解析

    我今天学习到SpringBoot里面自定义Bean的初始化与销毁方法 我先总结一下我学到的四种方法: 方法一: 指定init-method 和 destory-method 方法二: 通过让 Bean 实现 InitializingBean 接口,定义初始化逻辑 DisposableBean 接口,定义销毁逻辑 方法三: 用 @PostConstruct,在 Bean 创建完成并且赋值完成后,执行该注解标注的方法 @PreDestroy,在容器销毁 Bean 之前,执行该注解标注的方法 方法四:

  • 使用spring容器在初始化Bean时前和后的操作

    目录 spring容器初始化Bean操作 @PostConstruct和@PreDestroy注解 在XML中定义init-method和destory-method方法 Bean实现InitializingBean和DisposableBean接口 Spring bean 初始化顺序 1.概述 2.InitializingBean vs init-method 3.@PostConstruct 4.小结一下吧 spring容器初始化Bean操作 在某些情况下,Spring容器在初始化Bean的

  • 理解 MyBatis 是如何在 Spring 容器中初始化的

    MyBatis 初始化过程就是生成一些必须的对象放到 Spring 容器中.问题是这个过程到底生成了哪些对象?当遇到 MyBatis 初始化失败时,如何正确的找到分析问题的切入点?本文将针对这些问题进行介绍. 本文基于 MyBatis 3 和 Spring,假设读者已经知道如何使用 Maven 和 MyBatis,以及了解 Spring 的容器机制. 一.Mybatis 三件套 我们知道 MyBatis 的主要功能是由 SqlSessionFactory 和 Mapper 两者提供的,初始化 M

  • Spring容器中添加bean的5种方式

    目录 @Configuration + @Bean @Componet + @ComponentScan @Import注解导入 @Import直接导入类 @Import + ImportSelector @Import + ImportBeanDefinitionRegistrar @Import + DeferredImportSelector 使用FactoryBean接口 使用 BeanDefinitionRegistryPostProcessor 小结 我们知道平时在开发中使用Spri

  • 如何动态替换Spring容器中的Bean

    目录 动态替换Spring容器中的Bean 原因 方案 实现 Spring中bean替换问题 动态替换Spring容器中的Bean 原因 最近在编写单测时,发现使用 Mock 工具预定义 Service 中方法的行为特别难用,而且无法精细化的实现自定义的行为,因此想要在 Spring 容器运行过程中使用自定义 Mock 对象,该对象能够代替实际的 Bean 的给定方法. 方案 创建一个 Mock 注解,并且在 Spring 容器注册完所有的 Bean 之后,解析 classpath 下所有引入该

  • spring容器初始化遇到的死锁问题解决

    前言 最近启动spring项目的时候遇到一个死锁问题,使用jstack获取线程堆栈的时候,可以看到2个线程出现了死锁: 解决过程: DefaultSingletonBeanRegistry.getSingleton()源码如下,可以看到这个方法需要对singletonObjects加锁 第二处xxx.subject.core.cache.DataLocalcacheInit.afterPropertiesSet源码如下: 可以看到:这个bean在初始化的时候,会开启线程,调用另外一个bean的i

  • spring入门教程之bean的继承与自动装配详解

    Spring之Bean的基本概念 大家都知道Spring就是一个大型的工厂,而Spring容器中的Bean就是该工厂的产品.对于Spring容器能够生产那些产品,则取决于配置文件中配置. 对于我们而言,我们使用Spring框架所做的就是两件事:开发Bean.配置Bean.对于Spring矿建来说,它要做的就是根据配置文件来创建Bean实例,并调用Bean实例的方法完成"依赖注入". Bean的定义 <beans-/>元素是Spring配置文件的根元素,<bean-/&

  • Java 如何从spring容器中获取注入的bean对象

    1.使用场景 控制层调用业务层时,控制层需要拿到业务层在spring容器中注入的对象 2.代码实现 import org.apache.struts2.ServletActionContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.suppo

  • spring容器启动实现初始化某个方法(init)

    spring容器启动 初始化某方法(init) 1.前言 很多时候,我们需要在项目启动的时候,就要完成某些方法的执行.今天整理了一个简单的方法,使用spring容器中bean的属性:init-method 2.代码 /* 初始化的类.这里不需要添加任何注解 */ public class InitData { @Autowired private UserService userService; /* 初始化方法 */ public void inits(){ System.out.printl

  • Spring通过工具类实现获取容器中的Bean

    目录 1. Aware 接口 2. BeanFactoryAware 3. TienChin 项目实践 1. Aware 接口 小伙伴们知道,Spring 容器最大的特点在于所有的 Bean 对于 Spring 容器的存在是没有意识的,因此我们常说理论上你可以无缝将 Spring 容器切换为其他容器(然而在现实世界中,我们其实没有这样的选择,除了 Spring 容器,难道还有更好用的?). 当然这只是一个理论,在实际开发中,我们往往要用到 Spring 容器为我们提供的诸多资源,例如想要获取到容

随机推荐