Spring高级进阶:BeanFactoryPostProcessor

Easter79
• 阅读 579

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

一、bean生命周期简述

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

Spring高级进阶:BeanFactoryPostProcessor

步骤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的框架程序,也可以采用类似方案。

参考:bean生命周期

代码已上传:https://github.com/shuangzh/springinsight.git

 来源:周双的博客, 欢迎转载。(微信/QQ: 48689547, email:shuangzh@qq.com

 Spring高级进阶:BeanFactoryPostProcessor

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Easter79 Easter79
2年前
spring中bean配置和bean注入
1bean与spring容器的关系!(http://images2015.cnblogs.com/blog/633531/201604/633531201604241051017411539223365.jpg)Bean配置信息定义了Bean的实现及依赖关系,Spring容器根据各种形式的Bean配置信
Easter79 Easter79
2年前
spring中策略模式使用
策略模式工作中经常使用到策略模式工厂模式,实现一个接口多种实现的灵活调用与后续代码的扩展性。在spring中使用策略模式更为简单,所有的bean均为spring容器管理,只需获取该接口的所有实现类即可。下面以事件处理功能为例,接收到事件之后,根据事件类型调用不同的实现接口去处理。如需新增事件,只需扩展实现类即可,无需改动之前的代码。这样即
Stella981 Stella981
2年前
MyBatis MapperScannerConfigurer配置
MyBatisMapperScannerConfigurer配置博客分类:mybatisMybatisMapperScannerConfigurer自动扫描将Mapper接口生成代理注入到SpringMybatis在与Spring集成的时候可以配置MapperFactoryBean来生成Mapper接口的代理.例如<bea
Stella981 Stella981
2年前
Spring IOC
springioc是spring的核心之一,也是spring体系的基础,那么springioc所依赖的底层技术是什么的?反射,以前我们开发程序的时候对象之间的相互调用需要用new来实现,现在所有的bean都是通过spring容器来管理。这样做有什么好处呢?解耦!以前程序直接的调用用new直接给写死了,现在我们可以通过注入不同的接口实现类来完成对象直接的调
Easter79 Easter79
2年前
Spring的BeanFactoryPostProcessor接口
接口简介BeanFactoryPostProcessor接口是Spring初始化BeanFactory时对外暴露的扩展点,SpringIoC容器允许BeanFactoryPostProcessor在容器实例化任何bean之前读取bean的定义,并可以修改它。BeanDefinitionRegistryPostPro
Stella981 Stella981
2年前
MapperScannerConfigurer 作用详解
自动扫描将Mapper接口生成代理注入到Spring<!DAO接口所在包名,Spring会自动查找其下的类<beanclass"org.mybatis.spring.mapper.MapperScannerConfigurer"<propertyname"basePackage"va
Easter79 Easter79
2年前
Spring高级应用之注入嵌套Bean
在Spring中,如果某个Bean所依赖的Bean不想被Spring容器直接访问,可以使用嵌套Bean。和普通的Bean一样,使用<bean元素来定义嵌套的Bean,嵌套Bean只对它的外部的Bean有效,Spring容器无法直接访问嵌套的Bean,因此定义嵌套Bean也无需指定id属性。如下配置片段是一个嵌套Bean的示例:<bean id
京东云开发者 京东云开发者
6个月前
带着问题去分析:Spring Bean 生命周期 | 京东物流技术团队
1:Bean在Spring容器中是如何存储和定义的Bean在Spring中的定义是org.springframework.beans.factory.config.BeanDefinition接口,BeanDefinition里面存储的就是我们编写的Jav
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k