详解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规则,后面定义的会覆盖前面定义的(执行的时候只执行最后一个). ...
随机推荐
- win10 配置tensorflow环境
1. 在anaconda中新增环境 python3.5, 我使用的是anaconda-navigator 中新增的环境,python版本选择3.5 2. 激活新增加的环境, 注意win下,没有sour ...
- 如何将post请求转换成put和delete请求
<form:form action="${pageContext.request.contextPath}/emp" method="POST" mode ...
- JS 替换所有的空格
在JS中替换掉输入框内的空格,是在处理表单需求的时候极为常用的一项操作,以防止用户的操作习惯引起数据异常,保证传参的安全性. NO.1 name.replace(" "," ...
- 【洛谷T89359 扫雷】
题目链接 这个题有一个十分简单的做法 我们可以考虑位运算 按位异或(^) 异或,英文为exclusive OR,缩写成xor 异或(xor)是一个数学运算符.它应用于逻辑运算.异或的数学符号为“⊕”, ...
- easyhook源码分析一
easyhook简要说明: easyhook是一个开源的hook库(http://easyhook.github.io/),其支持托管代码(.NET)和非托管代码(C/C++)hook,这里只分析了其 ...
- 来自数据源的 String 类型的给定值不能转换为指定目标列的类型 nvarchar
.TrimEnd() 怀疑是否SqlBulkCopy是否存在某种bug,故而在系统中改写代码,用单个sql的插入数据方式,用循环逐条导入.结果是没问题.难道真的是SqlBulkCopy有某种bug?上 ...
- mysql5.7无法启动原因排查
前天刚刚拷了同事最新的MySQL5.7,安装成功后运行良好,今天却无法启动,Navicat也无法连接. 开始排查原因: 1.进入dos命令窗口,输入net start mysql启动,提示 百度出现错 ...
- 20191127 Spring Boot官方文档学习(4.14-4.17)
4.14.使用RestTemplate调用REST服务 如果需要从应用程序调用远程REST服务,则可以使用Spring Framework的RestTemplate类.由于RestTemplate实例 ...
- P2672跳石头
这是2015noip的一道二分答案的题目,看了题解才会,, 题目给出石头的位置并且让你踩着石头往前跳,最多删掉m个石头还可以顺利通过,求解最短跳跃距离的最大值. 那么二分什么呢:mid为跳跃的长度.那 ...
- 【Qt笔记】QAction与QToolButton的关联
QAction可以理解为一个动作数据,包含了这个同坐相关的图标.文本.是否可用等数据和状态,以及连接对应的槽函数,用于执行这个动作. QToolButton则可以使用QAction对象作为后端,显示这 ...