zoukankan      html  css  js  c++  java
  • 【转】Spring高级进阶:BeanFactoryPostProcessor

    转载自:周双的博客

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

  • 相关阅读:
    安装GitLab出现ruby_block[supervise_redis_sleep] action run
    docker tag根据镜像id做标签,用于应用的回滚
    Jenkenis报错:该jenkins实例似乎已离线
    kubernetes-批量删除Evicted Pods
    阿里云k8s构建镜像时设置版本号用于版本回滚
    Java虚拟机6:内存溢出和内存泄露、并行和并发、Minor GC和Full GC、Client模式和Server模式的区别
    Java虚拟机5:Java垃圾回收(GC)机制详解
    Java虚拟机4:内存溢出
    Java虚拟机3:常用JVM命令参数
    Java虚拟机2:Java内存区域及对象
  • 原文地址:https://www.cnblogs.com/UUUz/p/12170419.html
Copyright © 2011-2022 走看看