• Spring注解驱动之FactoryBean注册组件


    概述

    经过前面的学习,我们知道可以通过多种方式向Spring容器中注册bean。可以使用@Configuration注解结合@Bean注解向Spring容器中注册Bean;可以按照条件向Spring容器中注册bean;可以使用@Import注解向容器中快速导入bean对象;可以在@Import注解中使用ImportBeanDefinitionRegistrar向容器中注册bean。
    本文来讲讲如何使用FactoryBean向Spring容器中注册bean。

    FactoryBean概述

    一般情况下,Spring是通过反射机制利用bean的class属性指定实现类来实例化bean的。在某些情况下,实例化bean过程比较复杂,如果按照传统的方式,则需要在标签中提供大量的配置信息,配置方式的灵活性是受限的,这是采用编码的方式可以的得到一个更加简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化bean的逻辑。
    FactoryBean接口对应于Spring框架来说占有非常重要的地位,Spring自身就提供了70多个FactoryBean接口的实现。它们隐藏了实例化一些复杂bean的细节,给上层应用带来了便利。从Spring3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean的形式。
    在spring4.3.12.RELEASE这个版本中,FactoryBean接的定义如下。

    public interface FactoryBean<T> {
    	T getObject() throws Exception;
    
    	Class<?> getObjectType();
    
    	boolean isSingleton();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • T getObject():返回由FactoryBean创建的bean实例,如果isSingleton()返回true,那么该实例会放到Spring容器中的单实例缓存池中。
    • boolean isSingleton():返回由FactoryBean创建的bean实例的作用域是singleton还是prototype。
    • Class getObjectType():返回FactoryBean创建的bean实例的类型。

    注意:当配置文件中标签的class属性配置的实现类是FactoryBean时,通过getBean()方法返回的不是FactoryBean本身,而是FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject代理了getBean方法。

    FactoryBean案例

    首先,创建一个ColorFactoryBean类,它得实现FactoryBean接口,如下所示。

    package com.meimeixia.bean;
    
    import org.springframework.beans.factory.FactoryBean;
    
    /**
     * 创建一个Spring定义的FactoryBean
     * T(泛型):指定我们要创建什么类型的对象
     * @author liayun
     * 
     */
    public class ColorFactoryBean implements FactoryBean<Color> {
    
    	// 返回一个Color对象,这个对象会添加到容器中
    	@Override
    	public Color getObject() throws Exception {
    		System.out.println("ColorFactoryBean...getObject...");
    		return new Color();
    	}
    
    	@Override
    	public Class<?> getObjectType() {
    		return Color.class; // 返回这个对象的类型
    	}
    
    	// 是单例吗?
    	// 如果返回true,那么代表这个bean是单实例,在容器中只会保存一份;
    	// 如果返回false,那么代表这个bean是多实例,每次获取都会创建一个新的bean
    	@Override
    	public boolean isSingleton() {
    		// TODO Auto-generated method stub
    		return false;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    然后,我们在配置类中加入ColorFactoryBean的声明,如下所示。

    package com.meimeixia.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Conditional;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    import org.springframework.context.annotation.Lazy;
    
    import com.meimeixia.bean.Color;
    import com.meimeixia.bean.ColorFactoryBean;
    import com.meimeixia.bean.Person;
    import com.meimeixia.bean.Red;
    import com.meimeixia.condition.LinuxCondition;
    import com.meimeixia.condition.MyImportBeanDefinitionRegistrar;
    import com.meimeixia.condition.MyImportSelector;
    import com.meimeixia.condition.WindowsCondition;
    
    // 对配置类中的组件进行统一设置
    @Conditional({WindowsCondition.class}) // 满足当前条件,这个类中配置的所有bean注册才能生效
    @Configuration
    @Import({Color.class, Red.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class}) // @Import快速地导入组件,id默认是组件的全类名
    public class MainConfig2 {
    	
    	@Lazy
    	@Bean("person")
    	public Person person() {
    		System.out.println("给容器中添加咱们这个Person对象...");
    		return new Person("美美侠", 25);
    	}
    
    	@Bean("bill")
    	public Person person01() {
    		return new Person("Bill Gates", 62);
    	}
    	
    	@Conditional({LinuxCondition.class})
    	@Bean("linus")
    	public Person person02() {
    		return new Person("linus", 48);
    	}
    
    	@Bean
    	public ColorFactoryBean colorFactoryBean() {
    		return new ColorFactoryBean();
    	}	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    这里需要小伙伴们注意的是:这里使用@Bean注解向Spring容器中注册的是ColorFacotryBean对象。
    那现在我们就来看看Spring容器中到底都有哪些bean。
    在这里插入图片描述
    可以看到,结果信息中输出了一个colorFactoryBean,我们看下这个colorFactoryBean到底是什么类型。

    @Test
    public void testImport() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
        String[] definitionNames = applicationContext.getBeanDefinitionNames();
        for (String name : definitionNames) {
            System.out.println(name);
        }
        
        // 工厂bean获取的是调用getObject方法创建的对象
        Object bean2 = applicationContext.getBean("colorFactoryBean");
        System.out.println("bean的类型:" + bean2.getClass());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    再次运行测试类。
    在这里插入图片描述
    可以看到,虽然我们代码中使用@Bean注解注入的是ColorFactoryBean对象,但是实际上从Spring容器中获取到的bean对象却是调用ColorFactoryBean类中的getObject()方法获取到的Color对象。

    在Spring容器中获取到FactoryBean对象本身

    之前,我们使用@Bean注解向Spring容器中注册的是ColorFactoryBean,获取出来的却是Color对象。那么,如何才能获取ColorFactoryBean实例呢?
    其实,很简单,只需要在获取工厂Bean本身时,在id前面加上&符号即可,例如&colorFactoryBean

    public void testImport() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
        String[] definitionNames = applicationContext.getBeanDefinitionNames();
        for (String name : definitionNames) {
            System.out.println(name);
        }
        
        // 工厂bean获取的是调用getObject方法创建的对象
        Object bean2 = applicationContext.getBean("colorFactoryBean");
        Object bean3 = applicationContext.getBean("colorFactoryBean");
        System.out.println("bean的类型:" + bean2.getClass());
        System.out.println(bean2 == bean3);
        
        Object bean4 = applicationContext.getBean("&colorFactoryBean");
        System.out.println(bean4.getClass());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    运行测试方法,得到如下结果。
    在这里插入图片描述
    那问题来了,为什么在id前面加上&符号就会获取到ColorFactoryBean实例对象呢?
    打开BeanFactory接口,查看其源码
    在这里插入图片描述
    在BeanFactory接口中定义了一个&前缀,只要我们使用&+bean的id,Spring就会知道我们是在获取FactoryBean本身。

    参考

    Spring注解驱动开发第11讲——面试官让我说说:如何使用FactoryBean向Spring容器中注册bean?

  • 相关阅读:
    k8s apiserver启动执行流程之aggregatorServer
    安徽省图书馆典藏《乡村振兴振兴战略下传统村落文化旅游设计》许少辉八一新著
    【DC-DC】AP9180 内置 MOS 管升压型恒流驱动芯片
    《JavaSE-第十六章》之ArrayList源码与扩容机制
    十六、代码校验(4)
    vue中使用图像编辑器tui-image-editor(二)——应用示例
    java计算机毕业设计校园表白墙服务平台源程序+mysql+系统+lw文档+远程调试
    react路由根据用户角色设置权限校验
    Springboot 打包神器Maven 保姆级教程
    让reviewdog支持gitlab-push-commit,守住代码质量下限
  • 原文地址:https://blog.csdn.net/tianzhonghaoqing/article/details/126678849