详解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规则,后面定义的会覆盖前面定义的(执行的时候只执行最后一个). ...
随机推荐
- 待处理bug
https://laravel-china.org/docs/laravel/5.1/installation/1039#installation composer 下载laravel 有问题
- Python的sys.argv用法
import sys a = sys.argv[:] print("输入的参数为:", a) def train_start(start_time, end_time, selec ...
- python调用c++类方法(2)
testpy.cpp: #include<iostream> #include<vector> struct point{ float pointx; float pointy ...
- SpringMvc中ModelAndView模型的应用
/** * 目标方法的返回值可以是 ModelAndView 类型. * 其中可以包含视图和模型信息 * SpringMVC 会把 ModelAndView 的 model 中数据放入到 reques ...
- python读取文件乱码
方法一:使用codecs import codecs f = codecs.open('nlpir/Readme.txt','r','GBK') line = f.readline() while l ...
- linux shell 中"2>&1"含义-完美理解-费元星
笨鸟先飞,先理解. 脚本是: nohup /mnt/Nand3/H2000G >/dev/null 2>&1 & 对于& 1 更准确的 ...
- 【python】集合 list差集|并集|交集
两个list差集 list(set(b).difference(set(a))) # b中有而a中没有的 示例: a=[1,2,3] b=[2,3] list(set(a).difference(se ...
- rocketMQ 通信协议格式
rocketMQ 使用 netty 通信,端对端的通信,为了避免粘包.分包,需要指定发送数据的边界. 使用的解码器是 LengthFieldBasedFrameDecoder // org.apach ...
- Selenium学习之==>三种等待方式
在UI自动化测试中,必然会遇到环境不稳定,网络慢的情况,这时如果你不做任何处理的话,代码会由于没有找到元素,而报错.这时我们就要用到wait(等待),而在Selenium中,我们可以用到一共三种等待, ...
- SAP简介
1. 什么是SAP SAP的英文全名为System Application and Products in Data Processing.SAP既是公司名称,又是其产品的软件名称. 2. SAP的诞 ...