转载: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的更多相关文章

  1. Spring生命周期 Constructor > @PostConstruct > InitializingBean > init-method

    项目中用到了 afterPropertiesSet: 于是具体的查了一下到底afterPropertiesSet到底是什么时候执行的.为什么一定要实现 InitializingBean; **/ @C ...

  2. Spring InitializingBean init-method @PostConstruct 执行顺序

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

  3. 003-Spring4 扩展分析-spring类初始化@PostConstruct > InitializingBean > init-method、ApplicationContext、BeanPostProcessor、BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor

    一.spring类初始化@PostConstruct > InitializingBean > init-method InitializingBean接口为bean提供了初始化方法的方式 ...

  4. Bean初始化操作initMethod、@PostConstruct和InitializingBean

    我最新最全的文章都在南瓜慢说 www.pkslow.com,欢迎大家来喝茶! 1 简介 很多时间当一个Bean被创建出来后,我们希望做一些初始化操作,如初始化数据.缓存预热等.有以下三种方法: 初始化 ...

  5. Spring中的BeanPostProcessor详解

    Spring中的BeanPostProcessor详解 概述 BeanPostProcessor也称为Bean后置处理器,它是Spring中定义的接口,在Spring容器的创建过程中(具体为Bean初 ...

  6. Spring IoC createBean 方法详解

    前言 本篇文章主要分析 Spring IoC 的 createBean() 方法的流程,以及 bean 的生命周期. 下面是一个大致的流程图: 正文 AbstractAutowireCapableBe ...

  7. Spring源码分析之Bean的创建过程详解

    前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostProcessor调用过程详解 本文内容: 在 ...

  8. Spring框架系列(8) - Spring IOC实现原理详解之Bean实例化(生命周期,循环依赖等)

    上文,我们看了IOC设计要点和设计结构:以及Spring如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中的:容器中存放的是Bean的定义即Be ...

  9. Shiro的Filter机制详解---源码分析

    Shiro的Filter机制详解 首先从spring-shiro.xml的filter配置说起,先回答两个问题: 1, 为什么相同url规则,后面定义的会覆盖前面定义的(执行的时候只执行最后一个). ...

随机推荐

  1. 记录一下WPF中自寄宿asp.net服务添加urlacl的问题

    asp.net公开服务地址时,由于当前用户权限问题,会导致服务地址未添加到urlacl池中报错. 关于添加urlacl的细节,请参考我之前的文章:asp.net self host and urlac ...

  2. 6、Shiro之自定义realm

    1.创建一个包存放我们自定义的realm文件: 创建一个类名为CustomRealm继承AuthorizingRealm并实现父类AuthorizingRealm的方法,最后重写: CustomRea ...

  3. gitlab webhook jenkins 403问题解决方案

    1.gitlab webhook 403问题,一般描述为Error 403 anonymous is missing the Job/Build Permission 解决方法: 安装插件:gitla ...

  4. 异步分发任务celery

    Celery简介 Celery是一个功能完备即插即用的任务队列.它使得我们不需要考虑复杂的问题,使用非常简单. celery适用异步处理问题,当遇到发送邮件.或者文件上传, 图像处理等等一些比较耗时的 ...

  5. Spring Cloud的几个组件

    在微服务架构中,需要几个基础的服务治理组件,包括服务注册与发现.服务消费.负载均衡.断路器.智能路由.配置管理等,由这几个基础组件相互协作,共同组建了一个简单的微服务系统.一个简答的微服务系统如下图: ...

  6. 阶段3 1.Mybatis_09.Mybatis的多表操作_1 mybatis表之间关系分析

    4.mybatis中的多表查询     表之间的关系有几种:         一对多         多对一         一对一         多对多     举例:         用户和订单 ...

  7. VS2012下std::function的BUG解决办法

    VS2012版本下std::function存在问题,链接:https://stackoverflow.com/questions/13096162/stdfunction-not-compiling ...

  8. windows,oracle,dg报错:ORA-12528,ORA-12154,ORA-10456 ,PING[ARC1]: Heartbeat failed to connect to standby 'orclbk'. Error is 12154

    windows,oracle,dg报错:ORA-12528,ORA-12154,ORA-10456 最近有需求在windows的2台oracle服务器上搭建dg,在过程中遇到了一些错误,跟在linux ...

  9. c# 排列组合代码类

    /// <summary> /// 排列组件算法类 /// </summary> /// <typeparam name="T"></ty ...

  10. 应用安全 - Web安全 - 远程控制管理工具 - 汇总

    菜刀 蚁剑 冰蝎 DarkCommet ADT windows/upexec/reverse_tcp set PEXEC xxx