BeanFactoryPostProcessor是实现spring容器功能扩展的重要接口,例如修改bean属性值,实现bean动态代理等。很多框架都是通过此接口实现对spring容器的扩展,例如mybatis与spring集成时,只定义了mapper接口,无实现类,但spring却可以完成自动注入,是不是很神奇? 本文将通过简单的例子,展现BeanFactoryPostProcessor的扩展能力。

 一、bean生命周期简述

Spring Bean生命周期比较复杂,在此简化一下,如下图。

步骤1、 豆子工厂(BeanFactory)从xml文件、java配置或注解配置中读取“各种豆子的生产方法说明(BeanDefinition)”。

步骤2、 这些豆子分为“特殊豆子(实现spring指定的某些接口)”和“普通豆子”,  豆子工厂先生产出这些特殊豆子。

步骤3和4、 特殊豆子调用特定接口(例如BeanFactoryPostProcessor接口),可以对豆子工厂(BeanFactory)进行修改,或添加一些新豆子生产方法(即注册新的BeanDefinition到BeanFactory中)。

步骤5、豆子工厂(BeanFactory)执行getBean方法生产其他的普通裸豆子。(调用类的构造方法,或FactoryBean的getObject方法,以及@Bean注解的方法)

步骤6、设置豆子的依赖关系以及属性值。

步骤7、调用豆子的@PostConstruct指定的方法

步骤8、调用豆子的InitializingBean接口方法

步骤9、调用豆子的initMethod指定的方法。

总结上述过程, 我们可以得到以下执行顺序 :  BeanFactoryPostProcessor ---> 普通Bean构造方法 ---> 设置依赖或属性 ---> @PostConstruct ---> InitializingBean ---> initMethod 。

二、BeanFactoryPostProcessor  代码例子

BenzCar类(奔驰汽车类)有成员属性Engine(发动机), Engine是接口,无具体的实现类。本代码例子,通过BeanFactoryPostProcessor ,FactoryBean,动态代理三项技术实现给BenzCar装配上Engine。

首先是 SpringBoot的 App类,如下:

 1 @SpringBootApplication
2 public class App {
3
4 public static void main(String[] args) {
5 SpringApplication.run(App.class, args);
6 try {
7 System.in.read();
8 } catch (IOException e) {
9 e.printStackTrace();
10 }
11 }
12
13 @Bean(initMethod="start")
14 BenzCar benzCar(Engine engine){
15 BenzCar car = new BenzCar();
16 car.engine = engine;
17 return car ;
18 }
19 }

从上面第14行代码可以知道 benzCar 依赖 Engine对象,

以下是BenzCar 代码:

public class BenzCar implements InitializingBean {

    Engine engine;

    public BenzCar(){
System.out.println("BenzCar Constructor");
if(engine==null){
System.out.println("BenzCar's engine not setting");
}else{
System.out.println("BenzCar's engine installed");
}
} void start(){
System.out.println("BenzCar start");
engine.fire();
} @Override
public void afterPropertiesSet() throws Exception {
System.out.println("BenzCar initializingBean after propertieSet");
if(engine==null){
System.out.println("BenzCar's engine not setting, in initializingBean ");
}else{
System.out.println("BenzCar's engine installed, in initializingBean");
engine.fire();
}
} @PostConstruct
public void postConstruct(){
System.out.println("BenzCar postConstruct");
if(engine==null){
System.out.println("BenzCar's engine not setting, in postConstruct");
}else{
System.out.println("BenzCar's engine installed, in postConstruct");
}
} }

BenzCar类中有一个Engine对象成员, 在start方法中调用Engine的fire方法。

Engine接口代码如下:

public interface Engine {
void fire();
}

Engine是一个接口,一般情况下,需要在App类中配置一个Engine的实现类bean才行,否则因为缺少Engine实例,spring启动时会报错。通过FactoryBean和动态代理,可以生成Engine接口的代理对象;结合BeanFactoryPostProcessor 接口,将FactoryBean动态添加到BeanFactory中,即可以给BenzCar配置上Engine接口代理对象。

为此新增一个 SpecialBeanForEngine类, 代码如下:

 1 public class SpecialBeanForEngine implements BeanFactoryPostProcessor, BeanNameAware{
2
3 String name;
4
5 @Override
6 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
7
8 BeanDefinitionRegistry bdr = (BeanDefinitionRegistry)beanFactory;
9 GenericBeanDefinition gbd = new GenericBeanDefinition();
10 gbd.setBeanClass(EngineFactory.class);
11 gbd.setScope(BeanDefinition.SCOPE_SINGLETON);
12 gbd.setAutowireCandidate(true);
13 bdr.registerBeanDefinition("engine01-gbd", gbd);
14 }
15
16 public static class EngineFactory implements FactoryBean<Engine>, BeanNameAware, InvocationHandler{
17
18 String name;
19
20 @Override
21 public Engine getObject() throws Exception {
22 System.out.println("EngineFactory to build Engine01 , EngineFactory :"+ name);
23 Engine prox = (Engine) Proxy.newProxyInstance(this.getClass().getClassLoader(),new Class[]{Engine.class}, this);
24 return prox;
25 }
26
27 @Override
28 public Class<?> getObjectType() {
29 return Engine.class;
30 }
31
32 @Override
33 public boolean isSingleton() {
34 return true;
35 }
36
37 @Override
38 public void setBeanName(String name) {
39 this.name = name;
40 }
41
42 @Override
43 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
44 System.out.println("here is invoke engine:"+method.getName());
45 return null;
46 }
47 }
48
49 @Override
50 public void setBeanName(String name) {
51 this.name =name;
52 }
53 }

上面代码 8 ~ 13行,在postProcessBeanFactory方法中添加了 EngineFactory.class类的Bean。 EngineFactory 是一个FactoryBean,代码21-24行在getObject()方法中,使用动态代理生产Engine接口的代理对象。

在App类中增加SpecialBeanForEngine  Bean, 如下

    @Bean
SpecialBeanForEngine specialBeanForEngine(){
return new SpecialBeanForEngine();
}

程序运行结果如下:

 1 SpecialBeanForEngine bean name :specialBeanForEngine
2 EngineFactory to build Engine01 , EngineFactory :engine01-gbd
3 BenzCar Constructor
4 BenzCar's engine not setting
5 BenzCar postConstruct
6 BenzCar's engine installed, in postConstruct
7 BenzCar initializingBean after propertieSet
8 BenzCar's engine installed, in initializingBean
9 here is invoke engine:fire
10 BenzCar start
11 here is invoke engine:fire

第1行: specialBeanForEngine  bean 先生成

第2行: EngineFactory 调用 getObject()方法生产 Engine代理对象

第3行、4行: BenzCar调用构造方法,此时 engine属性还未被设置。

第5行、6: BenzCar调用@PostConstruct注解的方法,此时engine属性已经设置。

第7行: BenzCar调用 InitializingBean接口方法。

第10行: BenzCar调用 initMethod指定的方法,

第11行: BenzCar调用了代理对象的方法,SpecialBeanForEngine 类中第44行代码。

运行结果与前面描述的bean生命周期一致。至此,我们完成了只有Engine接口的情况下,在BenzCar中注入了Engine对象。

总结,postProcessBeanFactory接口、FactoryBean、动态代理,三者结合,可以在运行时动态的给BeanFactory中增加Bean,非常灵活的对spring容器进行扩展。很多开源项目在与spring整合时采用了类似方法。如果我们想自己写一些结合spring的框架程序,也可以采用类似方案。

转载自:https://www.cnblogs.com/piepie/p/9061076.html

Spring高级进阶:BeanFactoryPostProcessor的更多相关文章

  1. 肝了75天,五万五千字,《Spring Boot 进阶》专栏文章整理成册,分享~

    前言 Spring Boot 这个专栏从早期的体系构建到写完,总共花费了七十五天,期间由于工作及个人原因停更了一段时间,没办法,工作实在太忙了. 很多人疑惑了,为什么源码介绍过了就结束了?高级的部分不 ...

  2. 潭州学院-JavaVIP的Javascript的高级进阶-KeKe老师

    潭州学院-JavaVIP的Javascript的高级进阶-KeKe老师 讲的不错,可以学习 下面是教程的目录截图: 下载地址:http://www.fu83.cn/thread-283-1-1.htm ...

  3. C#可扩展编程之MEF学习笔记(五):MEF高级进阶

    好久没有写博客了,今天抽空继续写MEF系列的文章.有园友提出这种系列的文章要做个目录,看起来方便,所以就抽空做了一个,放到每篇文章的最后. 前面四篇讲了MEF的基础知识,学完了前四篇,MEF中比较常用 ...

  4. 高级进阶DB2(第2版)——内部结构、高级管理与问题诊断

    <高级进阶DB2(第2版)——内部结构.高级管理与问题诊断> 基本信息 作者: 牛新庄    出版社:清华大学出版社 ISBN:9787302323839 上架时间:2013-7-3 出版 ...

  5. MEF高级进阶

    MEF高级进阶   好久没有写博客了,今天抽空继续写MEF系列的文章.有园友提出这种系列的文章要做个目录,看起来方便,所以就抽空做了一个,放到每篇文章的最后. 前面四篇讲了MEF的基础知识,学完了前四 ...

  6. Spring高级装配

    Spring高级装配 目录 一.Profile(根据开发环境创建对应的bean) 二.条件化的创建bean(根据条件创建bean) 三.处理自动装配歧义性(指定首选bean.限定符限制bean) 四. ...

  7. .Net高级进阶,在复杂的业务逻辑下,如何以最简练的代码,最直观的编写事务代码?

    本文将通过场景例子演示,来通俗易懂的讲解在复杂的业务逻辑下,如何以最简练的代码,最直观的编写事务代码. 通过一系列优化最终达到两个效果,1.通过代码块来控制事务(分布式事务),2.通过委托优化Tran ...

  8. 《Android高级进阶》读书笔记

    <Android高级进阶>是据我所知的市面上唯一一本技术工具书,比较的高大全,作者的目的是为了对全领域有个初步的概念 No1: 在Android系统中,拥有事件传递处理能力的类有以下三种 ...

  9. Spring高级装配(一) profile

    Spring高级装配要学习的内容包括: Spring profile 条件化的bean声明 自动装配与歧义性 bean的作用域 Spring表达式语言 以上属于高级一点的bean装配技术,如果你没有啥 ...

随机推荐

  1. android ------ 实现高德定位并获取相应信息 ( 最新版高德SDK 和 Android SDK版本)

    Android开发项目时常常会遇到定位这个功能, 很久以前写过一篇了,官方也更新了一些东西,我也更新下 以前使用的是jar包 导入来实现高德定位 老版本 链接:https://www.cnblogs. ...

  2. Flutter ListView 列表组件

    列表常见的情况: 1.垂直列表 2.垂直图文列表 3.横向列表 4.动态列表 名称 类型 说明 scrollDirection Axis Axis.horizontal 横向列表 Axis.verti ...

  3. Dart中的mixins

    /* mixins的中文意思是混入,就是在类中混入其他功能. 在Dart中可以使用mixins实现类似多继承的功能,with关键字 因为mixins使用的条件,随着Dart版本一直在变,这里讲的是Da ...

  4. OpenBLAS编译 Debug x64 Win10 vs2015

    OpenBLAS编译  Debug x64  Win10  vs2015 >------ 已启动生成: 项目: ZERO_CHECK, 配置: Debug x64 ------ > Che ...

  5. idea中copyright使用

    1,在idea中找到settings->Editor->copyright->copyright profiles,然后点击+,输入名字,在copyright text中输入模板.然 ...

  6. php调用webservice报错Class 'SoapClient' not found(转)

    php在调用webservice时,报告如下类似错误: ( ! ) Fatal error: Class 'SoapClient' not found in E:/WebSrv/CI/system/l ...

  7. LeetCode_345. Reverse Vowels of a String

    345. Reverse Vowels of a String Easy Write a function that takes a string as input and reverse only ...

  8. 【计算机视觉】OpenCV篇(9) - 轮廓(寻找/绘制轮廓)

    什么是轮廓? 轮廓是一系列相连的点组成的曲线,代表了物体的基本外形. 轮廓与边缘好像挺像的? 是的,确实挺像,那么区别是什么呢?简而言之,轮廓是连续的,而边缘并不全都连续(见下图示例).其实边缘主要是 ...

  9. Charles 抓包配置

    本文参考:charles 抓包配置 proxy setting (代理设置) 设置的主界面如下: 动态端口 启用动态端口选项来监听动态端口,每次查询启动时选择.这样可以避免与计算机上可能运行的其他网络 ...

  10. DVWA 安全测试靶机本地搭建

    前期的搭建步骤这里就不多做表述了,网上文章很多,这里主要讲后续会遇到的问题和需要修改的地方.   首先将config-inc.php.dist  修改为config-inc.php 设置Key值 $_ ...