详解InitializingBean、initMethod和@PostConstruct
转载:https://blog.csdn.net/nrsc272420199/article/details/95033223
1. InitializingBean、initMethod和@PostConstruct的作用
实现了InitializingBean接口的类,可以在该类被注入到spring容器时达到 某些属性先装配完成后,再去装配另一些属性 的能力。而initMethod和@PostConstruct也可以达到相同的目的。
注意: 上文是一种用法,但思维不要局限。比如说我们的一个类里有一个属性,但是该属性不支持Spring注入,只能通过Build或者new的方式创建,而我们又想在spring装配Bean的时候一起将该属性注入进来,那使用InitializingBean、initMethod或@PostConstruct再合适不过了。
2. initMethod和InitializingBean
2.1 从initMethod说起
进行过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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="person" class="com.nrsc.springstudy.c071_InitializingBean_initMethod_PostConstruct.beans.Cat"
init-method="init">
<property name="name" value="花花"></property>
</bean> </beans>
没错initMethod就是原来spring配置文件里bean标签上的init-method,而InitializingBean也是spring提供的接口,那它俩有什么关系呢?先看如下代码:
2.2 从一个栗子来看initMethod和InitializingBean
下面的类中包含了initMethod和InitializingBean它俩的用法
package com.nrsc.springstudy.c071_InitializingBean_initMethod_PostConstruct.beans; import org.springframework.beans.factory.InitializingBean; /**
* Created By: Sun Chuan
* Created Date: 2019/7/7 22:19
*/
public class Cat implements InitializingBean {
private String name; //构造方法-----创建对象时调用
public Cat() {
System.out.println("Cat......constructor............");
} //设置name属性时会调用
public void setName(String name) {
System.out.println("===cat=========setName========");
this.name = name;
} public String getName() {
return name;
} //在配置类中利用注解将initMethod指向下面的init方法----对应于initMethod的用法
public void init() {
System.out.println("Cat......init............");
} //继承了InitializingBean接口,需要实现afterPropertiesSet方法---对应于InitializingBean的用法
public void afterPropertiesSet() throws Exception {
System.out.println("Cat......afterPropertiesSet............");
}
}
配置类如下
package com.nrsc.springstudy.c071_InitializingBean_initMethod_PostConstruct.config; import com.nrsc.springstudy.c071_InitializingBean_initMethod_PostConstruct.beans.Cat;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; @Configuration
public class C071Config { @Bean(initMethod = "init")
public Cat buildCat() {
Cat cat = new Cat();
cat.setName("花花");
return cat;
}
}
启动类如下
import com.nrsc.springstudy.c071_InitializingBean_initMethod_PostConstruct.config.C071Config;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; /**
* Created By: Sun Chuan
* Created Date: 2019/7/7 22:14
*/
public class Test071_InitializingBean_initMethod_PostConstruct {
@Test
public void test01() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(C071Config.class);
System.out.println("IOC容器创建完成........");
}
}
运行结果

2.3 探秘initMethod和InitializingBean在spring创建bean过程中的执行流程
追踪spring装配bean的源码到AbstractAutowireCapableBeanFactory类,里面有一个方法doCreateBean为真正创建bean的方法,我把其关键代码摘出来并加上注释后,如下:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException { // Instantiate the bean.----即初始化bean的意思,BeanWrapper为所有bean的包装类
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
//创建对象----利用反射机制(结合动态代理或CGLIB代理)创建对象---》相当于new一个对象
instanceWrapper = createBeanInstance(beanName, mbd, args);
} try {
//给属性赋值---》可以简单的理解为调用各个属性的set方法为各个属性进行赋值
populateBean(beanName, mbd, instanceWrapper);
//配置bean,即在当前对象创建完成,并对某些属性赋完值之后在对该bean进行其他一些处理
//就比如会调用我们利用initMethod和InitializingBean指定的方法
//还比如前置增强---后置增强(之后的博客肯定会介绍到)
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
//将装配好的bean返回,最终将会被装配到spring容器
return exposedObject;
}
再跟一下initializeBean方法 — 所在类AbstractAutowireCapableBeanFactory
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
//前置增强处理----先有个概念,之后的博客肯定会介绍到
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
//真正调用initMethod和InitializingBean指定的方法
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()) {
//后置增强处理----先有个概念,之后的博客肯定会介绍到
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
继续跟踪invokeInitMethods方法 — 所在类AbstractAutowireCapableBeanFactory
看到下面的方法就很一目了然了:
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
//判断该bean是否实现了实现了InitializingBean接口
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
//不用管if-else是啥逻辑,反正就是如果实现了InitializingBean接口,则调用该bean的afterPropertiesSet方法
((InitializingBean) bean).afterPropertiesSet();
return null;
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
//不用管if-else是啥逻辑,反正就是如果实现了InitializingBean接口,则调用该bean的afterPropertiesSet方法
((InitializingBean) bean).afterPropertiesSet();
}
}
//判断是否指定了initMethod方法,如果指定了,则再调用指定的initMethod方法
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
//具体调用initMethod方法---用到了反射
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
2.4 总结
将上面的三个方法的逻辑抽离出来,大概就是下图的样子,initMethod和InitializingBean是spring提供的两种对类的属性进行装配的方式,initMethod和InitializingBean指定的方法运行顺序在普通属性装配之后,而initMethod指定的方法又在InitializingBean指定的方法之后。

3. 简单介绍@PostConstruct,并比较其与InitializingBean、initMethod的执行顺序
@PostConstruct不属于spring,它是JSR250定义的java规范,也就是说它是jdk的注解,但它也能完成和InitializingBean、initMethod一样的功能,更具体的就不再进行研究了,这里仅将其和InitializingBean、initMethod放在一起,进行一下简单测试,修改后的Cat类如下:
package com.nrsc.springstudy.c071_InitializingBean_initMethod_PostConstruct.beans; import org.springframework.beans.factory.InitializingBean; import javax.annotation.PostConstruct; /**
* Created By: Sun Chuan
* Created Date: 2019/7/7 22:19
*/
public class Cat implements InitializingBean {
private String name; //构造方法-----创建对象时调用
public Cat() {
System.out.println("Cat......constructor............");
} //设置name属性时会调用
public void setName(String name) {
System.out.println("===cat=========setName========");
this.name = name;
} public String getName() {
return name;
} //在配置类中利用注解将initMethod指向下面的init方法----对应于initMethod的用法
public void init() {
System.out.println("Cat......init............");
} //继承了InitializingBean接口,需要实现afterPropertiesSet方法---对应于InitializingBean的用法
public void afterPropertiesSet() throws Exception {
System.out.println("Cat......afterPropertiesSet............");
}
@PostConstruct
public void init2(){
System.out.println("Cat......@PostConstruct............");
}
}
运行结果

1、Spring为bean提供了两种初始化bean的方式,实现InitializingBean接口,实现afterPropertiesSet方法,或者在配置文件中通过init-method指定,两种方式可以同时使用。
2、实现InitializingBean接口是直接调用afterPropertiesSet方法,比通过反射调用init-method指定的方法效率要高一点,但是init-method方式消除了对spring的依赖。
3、如果调用afterPropertiesSet方法时出错,则不调用init-method指定的方法
运用:
有时候springboot自定义类@autowire注入为null的问题
解决方案:
@Component
public class AtoboPipeline implements Pipeline { @Autowired
private UrllistRepository urllistRepository; private static AtoboPipeline atoboPipeline;
@PostConstruct //通过@PostConstruct实现初始化bean之前进行的操作
public void init() {
atoboPipeline = this;
atoboPipeline.urllistRepository = this.urllistRepository;
// 初使化时将已静态化的testService实例化
}
... //使用的时候这样使用
atoboPipeline.urllistRepository.save(urlList);
}
需要注意:注入类的调用方法是
atoboPipeline.urllistRepository.save(urlList);
这种调用方法看着很怪异,不过管用。注入这个功能在 controller 外面都不支持。
详解InitializingBean、initMethod和@PostConstruct的更多相关文章
- Spring生命周期 Constructor > @PostConstruct > InitializingBean > init-method
项目中用到了 afterPropertiesSet: 于是具体的查了一下到底afterPropertiesSet到底是什么时候执行的.为什么一定要实现 InitializingBean; **/ @C ...
- Spring InitializingBean init-method @PostConstruct 执行顺序
Spring 容器中的 Bean 是有生命周期的,Spring 允许在 Bean 在初始化完成后以及 Bean 销毁前执行特定的操作,常用的设定方式有以下三种: 通过实现 Initializing ...
- 003-Spring4 扩展分析-spring类初始化@PostConstruct > InitializingBean > init-method、ApplicationContext、BeanPostProcessor、BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor
一.spring类初始化@PostConstruct > InitializingBean > init-method InitializingBean接口为bean提供了初始化方法的方式 ...
- Bean初始化操作initMethod、@PostConstruct和InitializingBean
我最新最全的文章都在南瓜慢说 www.pkslow.com,欢迎大家来喝茶! 1 简介 很多时间当一个Bean被创建出来后,我们希望做一些初始化操作,如初始化数据.缓存预热等.有以下三种方法: 初始化 ...
- Spring中的BeanPostProcessor详解
Spring中的BeanPostProcessor详解 概述 BeanPostProcessor也称为Bean后置处理器,它是Spring中定义的接口,在Spring容器的创建过程中(具体为Bean初 ...
- Spring IoC createBean 方法详解
前言 本篇文章主要分析 Spring IoC 的 createBean() 方法的流程,以及 bean 的生命周期. 下面是一个大致的流程图: 正文 AbstractAutowireCapableBe ...
- Spring源码分析之Bean的创建过程详解
前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostProcessor调用过程详解 本文内容: 在 ...
- Spring框架系列(8) - Spring IOC实现原理详解之Bean实例化(生命周期,循环依赖等)
上文,我们看了IOC设计要点和设计结构:以及Spring如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中的:容器中存放的是Bean的定义即Be ...
- Shiro的Filter机制详解---源码分析
Shiro的Filter机制详解 首先从spring-shiro.xml的filter配置说起,先回答两个问题: 1, 为什么相同url规则,后面定义的会覆盖前面定义的(执行的时候只执行最后一个). ...
随机推荐
- java基础--继承、实现、依赖、关联、聚合、组合的联系与区别
继承 指的是一个类或者接口继承另一个类或者接口,而且可以增加自己的新功能. 实现 指的是一个class类实现interface接口. 依赖 简单说,就是一个类中的方法用到了另一个类,一般依赖关系在ja ...
- symbol,iterator,generator
1.symbol是在ES6中引入的一种基本数据类型,因为symbol是不重复.唯一的数据特性,symbol设计是被用来表示对象内部的私有属性的. symbol.for与symbol.keyfo ...
- unity让碰撞只发生一次
碰撞发生在帧的开始,所以你可以检测到冲突,并在LateUpdate复位: private bool hasCollided = false; void OnCollisionEnter(Collisi ...
- 【Qt开发】事件循环与线程 二
事件循环与线程 二 Qt 线程类 Qt对线程的支持已经有很多年了(发布于2000年九月22日的Qt2.2引入了QThread类),Qt 4.0版本的release则对其所有所支持平台默认地是对多线程支 ...
- 深入分析微博密码加密and百万级数据采集分享(登录篇)
一.前言 此文章主要是对微博微博关键词的检索结果进行采集,但是微博的采集需要登陆,所以此程序分为登陆程序和爬虫程序两部分: 微博要实现规模性数据采集自然少不了大量账号,这就需购买大量账号以及批量登陆, ...
- c++ 加载库错误问题解决
转载自:http://blog.csdn.net/sahusoft/article/details/7388617 一般我们在Linux下执行某些外部程序的时候可能会提示找不到共享库的错误, 比如: ...
- SSIS包定时执行
企业管理器 --管理 --SQL Server代理 --右键作业 --新建作业 --"常规"项中输入作业名称 --"步骤"项 --新建 --"步骤名& ...
- Oracle Replace函数的简单使用
REPLACE ( char, search_string [, replace_string]) 如果没有指定replace_string 变量的值,那么当发现search_string 变量的 ...
- 最少多少人说谎(dp)
https://ac.nowcoder.com/acm/contest/1168/H 题意:n个学生,邓志聪想知道这些学生的考试情况,于是一个一个叫这些学生叫去办公室问他们,但是有些学生并没有讲真话, ...
- 洛谷 P2196 挖地雷 & [NOIP1996提高组](搜索,记录路径)
传送门 解题思路 就是暴力!!! 没什么好说的,总之,就是枚举每一个起点,然后暴力算一遍以这个点为起点的所有路径,在算的过程中,只要比目前找到的答案更优,就有可能是最后的答案,于是就把路径更新一遍,保 ...