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

作者:Bwz_Learning 时间:2021-07-01 05:49:24 

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());
   }
}

来源:https://blog.csdn.net/zbw18297786698/article/details/73656460

标签:spring,容器,初始化,Bean
0
投稿

猜你喜欢

  • Java通过反射将 Excel 解析成对象集合实例

    2023-05-22 18:49:02
  • Spring依赖注入与第三方Bean管理基础详解

    2022-09-08 20:45:55
  • 深入理解Java设计模式之命令模式

    2023-11-24 11:06:31
  • Android入门简单实例

    2021-07-26 16:34:04
  • 如何使用Jenkins编译并打包SpringCloud微服务目录

    2021-09-25 07:07:01
  • Java新手环境搭建 JDK8安装配置教程

    2023-11-25 17:23:10
  • C#中结构体定义并转换字节数组详解

    2023-01-23 11:07:50
  • 在Spring Boot中实现HTTP缓存的方法

    2023-10-06 14:18:16
  • Java实现获取内网的所有IP地址

    2023-01-01 07:48:56
  • Unity实现仿3D轮转图效果

    2023-11-24 12:26:56
  • Java编程经典小游戏设计-打砖块小游戏源码

    2021-07-08 01:17:28
  • Android开发必知 九种对话框的实现方法

    2022-10-23 07:47:26
  • 简单了解Spring beanfactory循环依赖命名重复属性

    2023-10-27 19:39:14
  • java接口返回参数按照请求参数进行排序方式

    2023-02-19 07:30:05
  • Android 将 android view 的位置设为右下角的解决方法

    2022-02-26 19:01:27
  • Spring拦截器HandlerInterceptor接口代码解析

    2022-09-05 10:51:04
  • 在C#中创建和读取XML文件的实现方法

    2021-12-20 08:10:45
  • Android入门之实现自定义Adapter

    2021-09-30 17:34:10
  • c#调用qq邮箱smtp发送邮件修改版代码分享

    2023-04-03 11:11:31
  • unity scrollRect实现按页码翻页效果

    2021-10-11 11:52:43
  • asp之家 软件编程 m.aspxhome.com