Spring中BeanFactory,FactoryBean,factory-bean,factory-method的区别

概述

我们日常基本的使用中对BeanFactory、FactoryBean、factory-method、facotry-bean基本上不需要关系,但是本着对技术的好奇、敬畏、面试的角度,我们还是要了解一下它们到底时什么,有什么用。以后在工作中可能会使用到哦。

BeanFactory

这个最容易理解,bean工厂,bean的工厂,其实就是我们日常说是的spring容器,所有被spring管理的bean都在其中。

FactoryBean

为什么要使用FactoryBean

我们有多种方式可以向Spring容器中注册bean。
可以使用@Configuration注解结合@Bean注解向Spring容器中注册bean;
可以按照条件向Spring容器中注册bean;
可以使用@Import注解向容器中快速导入bean对象;
可以在@Import注解中使用ImportBeanDefinitionRegistrar向容器中注册bean。

一般情况下,Spring是通过反射机制利用bean的class属性指定实现类来实例化bean的。
但是如果示例化bean的过程比较复杂,按照传统的方式需要在标签中提供大量的配置信息,配置方式的灵活性是受限的,如果能够采用编码的方式定制bean实例化,使用起来比较方便。
下面讲解如何使用FactoryBean向Spring容器中注册bean。
生产bean的工厂,可以理解为就是一个生成bean的代理类。是Spring的特殊接口,工厂类接口(FactoryBean),为了解决复杂的实例化bean过程而被创造的,新版本增加了泛型类应对所有类型。

public interface FactoryBean<T> {
    T getObject() throws Exception;  //返回由FactoryBean创建的Bean实例
    Class<T> getObjectType();
    boolean isSingleton();
}

通过这个接口,我们可以看出使用的就是工厂方法模式,不同的子类实现FactoryBean接口,生成具体的bean。

创建复杂bean时简化配置

spring中如果bean在多参数的情况下需要在中提供大量的配置信息。
例如:

public   class  Car  {  
        private   int maxSpeed ;  
        private  String brand ;  
        private   double price ;  
  //getter/setter方法省略...
}
<bean id ="car" class="xxx.Car">
        <property name="maxSpeed" ref="100"/>
        <property name="brand" ref="Suzuki"/>
        <property name="price" ref="100000"/> 
</bean>

可以看到当属性越多时,标签就越复杂。
当使用了FactoryBean作为工厂类来生产Car对象时:

// 工厂类,实现 FactoryBean<T>接口
public class CarFactoryBean implements FactoryBean<Car>  { 
    private  String carInfo ; 
    // 重写getObject()方法,创建Car实例
    public Car getObject () throws  Exception  {  
        Car car =  new  Car () ;  
        // 将carInfo信息以逗号形式分割,分给各个属性,返回实例
        String []  infos =  carInfo.split ( "," ) ;  
        car.setBrand ( infos [ 0 ]) ;  
        car.setMaxSpeed ( Integer. valueOf ( infos [ 1 ])) ;  
        car.setPrice ( Double. valueOf ( infos [ 2 ])) ;  
        return car;  
    }  
    public  Class<Car> getObjectType () {  
        return Car.class ;  
    }  
    public boolean isSingleton () {  
        return false ;  
    }  
    public String getCarInfo () {  
        return this.carInfo ;  
    }  
  
    // 接受逗号分割符设置属性信息  
    public void setCarInfo ( String carInfo )   {  
        this.carInfo = carInfo;  
    }  
}
<bean id="car" class="com.baobaotao.factorybean.CarFactoryBean">
    <property name="carInfo" value="法拉利,400,2000000"/>
</bean>

这样就极大的简化了标签的代码量,在配置多个bean,多参数的场景下很实用。
当调用getBean(“car”)时,Spring通过反射机制发现CarFactoryBean实现了FactoryBean的接口,这时Spring容器就调用方法CarFactoryBean#getObject()方法返回Car实例。
如果希望获取CarFactoryBean的实例,则需要使用getBean(“&car”),这是spring的内部约定:在需要获取FactoryBean时需要在getBean(beanName)方法是在beanName前显示的加上&前缀。
getBean(“car”) -> Car实例;
getBean(“&car”) -> CarFactoryBean实例;

简化依赖

当有其他类想要依赖Car类时:

public class Person { 
    private Car car ;
    private void setCar(Car car){ this.car = car;  }  
}// 这里用java config代替了<bean>标签,但这不是重点
@Configuration 
public class CarConfiguration { 
    @Bean 
    public CarFactoryBean carFactoryBean(){ 
      CarFactoryBean cfb = new CarFactoryBean();
      cfb.setBrand("Honda");
      cfb.setPiece(1984000);
      return cfb;
    }@Bean
    public Person aPerson(){ 
    	Person person = new Person();
      	// 注意这里的不同,这里直接调用carFactoryBean().getObject()方法返回Car实例化对象
   	 	person.setCar(carFactoryBean().getObject());
    	return person; 
    } 
}

小结

  • BeanFactory:是个Factory,是SpringIOC容器,是对象的工厂。
  • FactoryBean:是个Bean,一个能生产对象,修饰对象生产的工厂bean。

factory-method 与factory-bean

factory-method,factory-bean只有在spring中才有,springboot中不存在。
它们是标签的属性,用于创建bean对象,需要参数的话使用constructor-arg元素来指定它的参数。
factory-method可以用在静态方法创建类和非静态方法创建类两种,factory-bean用来指定创建bean的工厂bean,只能用在非静态方法创建bean的场景。
使用factory-method和factory-bean方式创建bean,我们可以自己定义一个类,然后在该类中定义一个方法,在该方法中完成spring bean的创建工作,其中factory-bean用于指定自己定义的类,factory-method用于指定创建bean的方法。

示例

  • 定义要创建的bean
public class Stu {
    public String stuId;
    public Stu(String stuId) {
        this.stuId = stuId;
    }
}
  • 定义工厂bean
public class StuFactory {
    static Map<String,Stu> stuMap = new HashMap<>();
    static{
        //初始化
        Stream.iterate(1,n->n+1).limit(5).map(String::valueOf).forEach(t-> stuMap.put(t, new Stu(t)));
    }
    
    //静态创建类,方法必须是静态(static修饰)
    public static Stu getStaticStu(String stuId){
        return stuMap.get(stuId);
    }

    //动态创建类
    public Stu getDynamicStu(String stuId){
        return new Stu(stuId);
    }
}

定义xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--静态获取对象-->
    <bean id="staticStu" class="yudaosourcecode.factorymethod.StuFactory" factory-method="getStaticStu">
        <!--传入getStaticStu方法的参数-->
        <constructor-arg value="1"/>
    </bean>

    <!--生成对象的工厂-->
    <bean id="stuFactory" class="yudaosourcecode.factorymethod.StuFactory"/>
    <!--动态获取对象-->
    <bean id="dynamicStu" factory-bean="stuFactory" factory-method="getDynamicStu">
        <!--传入getDynamicStu方法的参数-->
        <constructor-arg value="11"/>
    </bean>
</beans>
  • 测试
@Test
public void springFactoryMethodConfig() {
    ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:springFactoryMethodConfig.xml");
    //获取静态Bean
    Stu staticStu = ac.getBean("staticStu", Stu.class);
    System.out.println("staticStu: ");
    System.out.println(staticStu);
    //获取动态Bean
    Stu dynamicStu = ac.getBean("dynamicStu", Stu.class);
    System.out.println("dynamicStu: ");
    System.out.println(dynamicStu);
}
staticStu: 
yudaosourcecode.factorymethod.Stu@6ec8211c
dynamicStu: 
yudaosourcecode.factorymethod.Stu@7276c8cd

Process finished with exit code 0

参考

BeanFactory,FactoryBean;factory-bean,factory-method 区别

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

融极

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值