我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Spring注解驱动之BeanPostProcessor后置处理器讲解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Spring注解驱动之BeanPostProcessor后置处理器讲解

概述

在学习Spring的时候,在了解基本用法的时候,如果有时间一定要深入源码了解Spring的底层原理,这样在做一些适配工作、写一些轮子的时候就会比较容易,否则会很难,甚至一头雾水,无法完成工作。

吃透Spring的原理和源码,往往可以拉开人们之间的差距,当前只要是使用Java技术栈开发的Web项目,几乎都会使用Spring框架。

而且目前各招聘网站上对于Java开发的要求几乎清一色的都是熟悉或者精通Spring,所以,你很有必要学习Spring的细节知识点。

BeanPostProcessor后置处理器概述

首先,看下BeanPostProcessor的源码。

package org.springframework.beans.factory.config;

import org.springframework.beans.BeansException;

public interface BeanPostProcessor {
	Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

	Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

从源码可以看出,BeanPostProcessor是一个接口,其中有两个方法:

postProcessBeforeInitialization和postProcessAfterInitialization这两个方法分别是在Spring容器中的bean初始化前后执行,所以Spring容器中的每个bean对象初始化前后,都会执行BeanPostProcessor接口的两个方法。

也就是说,postProcessBeforeInitialization方法会在bean实例化和属性设置之后,自定义初始化方法之前被调用,而postProcessAfterInitialization方法会在自定义初始化方法之后被调用。当容器中存在多个BeanPostProcessor的实现类时,会按照它们在容器中注册的顺序执行。对于自定义的BeanPostProcessor实现类,还可以让其实现Ordered接口自定义排序。

因此我们可以在每个bean对象初始化前后,加上自己的逻辑。实现方式是自定义一个BeanPostProcessor接口的实现类,例如MyBeanPostProcessor,然后在该类的postProcessBeforeInitialization和postProcessAfterInitialization这两个方法写上自定义逻辑。

BeanPostProcessor后置处理器实例

我们创建一个MyBeanPostProcessor类,实现BeanPostProcessor接口,如下所示。

package com.meimeixia.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

@Component // 将后置处理器加入到容器中,这样的话,Spring就能让它工作了,否则无法工作
public class MyBeanPostProcessor implements BeanPostProcessor {

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("postProcessBeforeInitialization..." + beanName + "=>" + bean);
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("postProcessAfterInitialization..." + beanName + "=>" + bean);
		return bean;
	}
}

接下来,我们应该是要编写测试用例来进行测试了。

package com.meimeixia.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

import com.meimeixia.bean.Car;

@ComponentScan("com.meimeixia.bean")
@Configuration
public class MainConfigOfLifeCycle {

	@Bean(initMethod="init", destroyMethod="destroy")
	public Car car() {
		return new Car();
	}	
}

第二处改动是将Cat类上添加的@Scope(“prototype”)注解给注释掉,因为咱们之前做测试的时候,也是将Cat对象设置成多实例bean了。

package com.meimeixia.bean;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

// @Scope("prototype")
@Component
public class Cat implements InitializingBean, DisposableBean {
	
	public Cat() {
		System.out.println("cat constructor...");
	}

	
	@Override
	public void destroy() throws Exception {
		// TODO Auto-generated method stub
		System.out.println("cat destroy...");
	}

	
	@Override
	public void afterPropertiesSet() throws Exception {
		// TODO Auto-generated method stub
		System.out.println("cat afterPropertiesSet...");
	}
}

好了,现在咱们就可以编写测试用例来进行测试了。

可喜的是,我们也不用再编写一个测试用例了,直接运行IOCTest_LifeCycle类中的test01()方法就行,该方法的代码如下所示。

@Test
public void test01() {
    // 1. 创建IOC容器
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
    System.out.println("容器创建完成");
    // 关闭容器
    applicationContext.close();
}

此时,运行IOCTest_LifeCycle类中的test01()方法,输出的结果信息如下所示。

可以看到,postProcessBeforeInitialization方法会在bean实例化和属性设置之后,自定义初始化方法之前被调用,而postProcessAfterInitialization方法会在自定义初始化方法之后被调用。

当然了,也可以让我们自己写的MyBeanPostProcessor类来实现Ordered接口自定义排序,如下所示。

package com.meimeixia.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;


@Component // 将后置处理器加入到容器中,这样的话,Spring就能让它工作了
public class MyBeanPostProcessor implements BeanPostProcessor, Ordered {
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("postProcessBeforeInitialization..." + beanName + "=>" + bean);
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("postProcessAfterInitialization..." + beanName + "=>" + bean);
		return bean;
	}

	@Override
	public int getOrder() {
		return 3;
	}
}

BeanPostProcessor后置处理器作用

后置处理器可用于bean对象初始化前后进行逻辑增强。

Spring提供了BeanPostProcessor接口的很多实现类,例如AutowiredAnnotationBeanPostProcessor用于@Autowired注解的实现,AnnotationAwareAspectJAutoProxyCreator用于Spring AOP的动态代理等等。

除此之外,我们还可以自定义BeanPostProcessor接口的实现类,在其中写入咱们需要的逻辑。

bean的初始化和销毁流程

我们知道BeanPostProcessor的postProcessBeforeInitialization()方法是在bean的初始化之前被调用;而postProcessAfterInitialization()方法是在bean初始化的之后被调用。

并且bean的初始化和销毁方法我们可以通过如下方式进行指定。

1. 通过@Bean指定init-method和destroy-method

@Bean(initMethod="init", destroyMethod="destroy")

2. 通过让bean实现InitializingBean和DisposableBean这俩接口

@Componentpublic class Cat implements InitializingBean, DisposableBean {}

3. 使用JSR-250规范里面定义的@PostConstruct和@PreDestroy这俩注解

  • @PostConstruct:在bean创建完成并且属性赋值完成之后,来执行初始化方法
  • @PreDestroy:在容器销毁bean之前通知我们进行清理工作

4. 通过让bean实现BeanPostProcessor接口

@Component // 将后置处理器加入到容器中,这样的话,Spring就能让它工作了
public class MyBeanPostProcessor implements BeanPostProcessor, Ordered {}

通过以上四种方式就可以对bean的整个生命周期进行控制:

  • bean的实例化:调用bean的构造方法,我们可以在bean的无参构造方法中执行相应的逻辑。
  • bean的初始化:在初始化时可以通过BeanPostProcessor的postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法进行拦截,执行自定义的逻辑。通过@PostConstruct注解、InitializingBean和init-method来指定bean初始化前后执行的方法,在该方法中可以执行自定义的逻辑。
  • bean的销毁:可以通过@PreDestroy注解、DisposableBean和destroy-method来指定bean在销毁前执行的方法,在方法中可以执行自定义的逻辑。

所以,通过上述4种方式,我们可以控制Spring中bean的整个生命周期。

BeanPostProcessor源码解析

如果想深刻理解BeanPostProcessor的工作原理,那么就不得不看下相关的源码,我们可以在MyBeanPostProcessor类的postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法这两处打上断点来进行调试,如下所示。 

public class MyBeanPostProcessor implements BeanPostProcessor, Ordered {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization" + ",beanName:"+beanName + ",bean=>" + bean);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization" + ",beanName:"+beanName + ",bean=>" + bean);
        return bean;
    }

    @Override
    public int getOrder() {
        return 3;
    }
}

通过断点调试,我们可以看到,在applyBeanPostProcessorBeforeInitialization()方法中,会遍历所有BeanPostProcessor对象,然后依次执行所有BeanPostProcessor对象的postProcessorBeforeInitialization()方法,一旦BeanPostProcessor对象的postProcessBeforeInitialization()方法返回null以后,则后面的BeanPostProcessor对象便不再执行了,而是直接退出for循环。这些都是我们看源码看到的。

看Spring源码,我们还看到一个细节,在Spring中调用initializeBean()方法之前,还调用了populateBean()方法来为bean的属性赋值。

经过一系列的跟踪源码分析,我们可以将关键代码的调用过程使用如下伪代码表述出来。

populateBean(beanName, mbd, instanceWrapper); // 给bean进行属性赋值
initializeBean(beanName, exposedObject, mbd)
{
	applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
	invokeInitMethods(beanName, wrappedBean, mbd); // 执行自定义初始化
	applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}

也就是说,在Spring中,调用initializeBean()方法之前,调用了populateBean()方法为bean的属性赋值,为bean的属性赋好值之后,再调用initializeBean()方法进行初始化。

在initializeBean()中,调用自定义的初始化方法(即invokeInitMethods())之前,调用了applyBeanPostProcessorsBeforeInitialization()方法,而在调用自定义的初始化方法之后,又调用了applyBeanPostProcessorsAfterInitialization()方法。至此,整个bean的初始化过程就这样结束了。

BeanPostProcessor接口在Spring底层的应用案例

ApplicationContextAwareProcessor类

org.springframework.context.support.ApplicationContextAwareProcessor是BeanPostProcessor接口的一个实现类,这个类的作用是可以向组件中注入IOC容器,大致的源码如下。

注意:我这里的Spring版本为4.3.12.RELEASE。

那具体如何使用ApplicationContextAwareProcessor类向组件中注入IOC容器呢?

如果需要向组件中注入IOC容器,那么可以让组件实现ApplicationContextAware接口。

例如,我们创建一个创建一个Dog类,使其实现ApplicationContextAware接口,此时,我们需要实现ApplicationContextAware接口中的setApplicationContext()方法,在setApplicationContext()方法中有一个ApplicationContext类型的参数,这个就是IOC容器对象,我们可以在Dog类中定义一个ApplicationContext类型的成员变量,然后在setApplicationContext()方法中为这个成员变量赋值,此时就可以在Dog类中的

其他方法中使用ApplicationContext对象了,如下所示。

package com.meimeixia.bean;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;


@Component
public class Dog implements ApplicationContextAware {
	
	private ApplicationContext applicationContext;

	public Dog() {
		System.out.println("dog constructor...");
	}
	
	// 在对象创建完成并且属性赋值完成之后调用
	@PostConstruct
	public void init() { 
		System.out.println("dog...@PostConstruct...");
	}
	
	// 在容器销毁(移除)对象之前调用
	@PreDestroy
	public void destory() {
		System.out.println("dog...@PreDestroy...");
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { // 在这儿打个断点调试一下
		// TODO Auto-generated method stub
		this.applicationContext = applicationContext;
	}	
}

看到这里,相信不少小伙伴们都有一种很熟悉的感觉,没错,我之前也在项目中使用过!是的,这就是BeanPostProcessor在Spring底层的一种使用场景。至于上面的案例代码为何会在setApplicationContext()方法中获取到ApplicationContext对象,这就是ApplicationContextAwareProcessor类的功劳了!

接下来,我们就深入分析下ApplicationContextAwareProcessor类。

我们先来看下ApplicationContextAwareProcessor类中对于postProcessBeforeInitialization()方法的实现,如下所示。

在bean初始化之前,首先对当前bean的类型进行判断,如果当前bean的类型不是EnvironmentAware,不是EmbeddedValueResolverAware,不是ResourceLoaderAware,不是ApplicationEventPublisherAware,不是MessageSourceAware,也不是ApplicationContextAware,那么直接返回bean。

如果是上面类型中的一种类型,那么最终会调用invokeAwareInterfaces()方法,并将bean传递给该方法。

invokeAwareInterfaces()方法又是个什么呢?我们继续看invokeAwareInterfaces()方法的源码,如下所示。

可以看到invokeAwareInterfaces()方法的源码比较简单,就是判断当前bean属于哪种接口类型,然后将bean强转为哪种接口类型的对象,接着调用接口中的方法,将相应的参数传递到接口的方法中。

我们可以看到,此时会将this.applicationContext传递到ApplicationContextAware接口的setApplicationContext()方法中。所以,我们在Dog类的setApplicationContext()方法中就可以直接接收到ApplicationContext对象了。

BeanValidationPostProcessor类

org.springframework.validation.beanvalidation.BeanValidationPostProcessor类注意是用来为bean进行校验操作的,当我们创建bean,并为bean赋值后,我们可以通过BeanValidationPostProcessor类为bean进行校验操作。BeanValidationPostProcessor类源码如下:

这里,我们也来看看postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法的实现,如下所示。

InitDestroyAnnotationBeanPostProcessor类

org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor类主要用来处理@PostConstruct注解和@PreDestroy注解。

使用了@PostConstruct注解和@PreDestroy注解来标注方法,Spring怎么就知道什么时候执行@PostConstruct注解标注的方法,什么时候执行@PreDestroy注解标注的方法呢?这就要归功于InitDestroyAnnotationBeanPostProcessor类了。

接下来,我们也通过Debug的方式来跟进下代码的执行流程。首先,在Dog类的initt()方法上打上一个断点,如下所示。

在InitDestroyAnnotationBeanPostProcessor类的postProcessBeforeInitialization()方法中,首先会找到bean中有关生命周期的注解,比如@PostConstruct注解等,找到这些注解之后,则将这些信息赋值给LifecycleMetadata类型的变量metadata,之后调用metadata的invokeInitMethods()方法,通过反射来调用标注了@PostConstruct注解的方法。

这就是为什么标注了@PostConstruct注解的方法会被Spring执行的原因。

	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
		try {
			metadata.invokeInitMethods(bean, beanName);
		}
		catch (InvocationTargetException ex) {
			throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
		}
		catch (Throwable ex) {
			throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
		}
		return bean;
	}

AutowiredAnnotationBeanPostProcessor类

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor类主要是用于处理标注了@Autowired注解的变量或方法。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Spring注解驱动之BeanPostProcessor后置处理器讲解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录