转载: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. linux下面用Mingw编译libx264

    linux下面用Mingw编译libx264 首先要先安装好mingw 我用的是Ubuntu 编译ffmpeg的时候 ,官方上面有一个自动化脚本能够把mingw安装好 这里就不说了 新版本的libx2 ...

  2. React Native商城项目实战04 - 封装TabNavigator.Item的创建

    1.Main.js /** * 主页面 */ import React, { Component } from 'react'; import { StyleSheet, Text, View, Im ...

  3. 2017-03-04 idea破解

    https://blog.csdn.net/qq_27686779/article/details/78870816 防止原址被删除,备份下,亲测可用 http://idea.java.sx/ 简单快 ...

  4. 下载vuejs,Hello Vue(vscode)

    下载vuejs,Hello Vue(vscode) Hello Vue 下载使用vue.js 动图演示 Vue的基本使用步骤  传送门:https://cn.vuejs.org/v2/guide/in ...

  5. leetcode-mid-math-172. Factorial Trailing Zeroes-NO-????

    mycode 问题:为甚在小于200的时候,答案ok,大于等于200的时候,就少一个1??? class Solution(object): def trailingZeroes(self, n): ...

  6. sso单点登录原理详解

    sso单点登录原理详解     01 单系统登录机制    1.http无状态协议 web应用采用browser/server架构,http作为通信协议.http是无状态协议,浏览器的每一次请求,服务 ...

  7. 吃着火锅唱着歌学会Docker

    第一篇:Docker概述 第二篇:Docker之版本与安装 第三篇:Docker之镜像 第四篇:Docker之容器 第五篇:Dcoker之容器与镜像 第六篇:Docker之网络管理 第七篇:Docke ...

  8. Spring Cloud的几个组件

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

  9. 阶段3 1.Mybatis_06.使用Mybatis完成DAO层的开发_4 Mybatis中使用Dao实现类的执行过程分析-查询方法

    delete方法没有并SqlSession的delete方法,而是调用的Upadte方法. 在测试类这里加断点. 实际的方法体内也加断点 运行测试方法,选择debug的方式 走到断点这里.会看到fac ...

  10. linux下 sleep() 与 usleep()

    usleep() 将进程挂起一段时间, 单位是微秒(百万分之一秒): 头文件: unistd.h 语法: void usleep(int micro_seconds); 返回值: 无 内容说明:本函数 ...