BeanFactory与FactoryBean区别

面试中经常被问到这问题,事实上,很多人只会死记硬背记答案。百度一下这个问题,总结出来的答案大致如下:

BeanFactory,以Factory结尾,表示它是一个工厂类(接口),用于管理Bean的一个工厂。它是IOC容器的核心接口,用来管理和装配普通bean的ioc容器

FactoryBean,以Bean结尾,表示它是一个Bean,在IOC容器的基础上给Bean的实现加上了一个简单工厂模式和装饰模式,是一个可以生产对象和装饰对象的工厂bean,由spring管理后,生产的对象是由getObject()方法决定的。

然后百度了半天,得出一个模糊的结论,两个都是用来产生bean的,最后面试还是说的模棱两可。

其实概念并没有错,只是我们并不理解它的含义,现在让我们从代码层面来理解这两个东西。

在传统的xml配置文件时代,我们的bean对象是通过xml配置文件形式配置出来的,这里我们定义几个类,项目结构如下:

这里我们在dao包下定义了两个类,代码分别如下:

public class DaoFacrotyBean implements FactoryBean {

    private String name;
    private String sex;
    private Integer age;

    public void setName(String name) {
        this.name = name;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public Object getObject() throws Exception {
        UserFactoryBean userFactoryBean = new UserFactoryBean();
        userFactoryBean.setName(name);
        userFactoryBean.setSex(sex);
        userFactoryBean.setAge(age);
        return userFactoryBean;
    }

    @Override
    public Class<?> getObjectType() {
        return null;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }
}
public class UserFactoryBean {
    private String name;
    private String sex;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

可以看到,我们的DaoFactoryBean实现了FactoryBean接口,重写了接口的三个方法,重点关注的是getObject方法,可以看到,我们返回的是UserFactoryBean对象,并且设置了对应的属性,这里的属性值我们通过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="daoFactoryBean" class="com.rongdu.dao.DaoFacrotyBean">
        <property name="name" value="张三"></property>
        <property name="sex" value="男"></property>
        <property name="age" value="18"></property>
    </bean>

</beans>

AppConfig类代码如下 :

@Configuration
@ComponentScan(value = "com.rongdu")
@ImportResource("classpath:spring.xml")
public class AppConfig {
}

下面我们写一个测试类:

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new 			    AnnotationConfigApplicationContext(AppConfig.class);
        DaoFacrotyBean daoFactoryBean = (DaoFacrotyBean) applicationContext.getBean("daoFactoryBean");
        System.out.println(daoFactoryBean);
    }
}

这里我们用AnnotationConfigApplicationContext类获取xml配置文件中定义的daoFactoryBean对象,这时我们已经设置好了相关属性,最后输出结果如下所示:

通过上面的报错我们可以知道这里输出的是UserFactoryBean对象,可想而知,这里的getBean方法其实是调用的DaoFacrotyBean中的getObject方法,返回的是UserFactoryBean对象,同时我们通过xml配置了相应的属性。这是很重要的一点。

正确的写法是:

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        UserFactoryBean userFactoryBean = (UserFactoryBean) applicationContext.getBean("daoFactoryBean");
        System.out.println(userFactoryBean);
        System.out.println(userFactoryBean.getName());
        System.out.println(userFactoryBean.getSex());
        System.out.println(userFactoryBean.getAge());
    }
}

结果如下:

如果我们要引入第三方依赖,我们想把它交给spring管理,那么我们需要在xml配置好相应的bean,这看起来没什么问题,但是仔细想想,如果第三方的类依赖过多,换句话说,就像上面的UserFactoryBean一样,如果类的属性过多,或者类中引入的其他类、配置等属性过多,那么我们就无法在xml配置也不可能自己在xml中配置,这些工作必定是交给第三方完成的,我们只需要通过实现FactoryBean这个接口获取第三方已经配置好的对象。举个例子:

@Configuration
@ComponentScan(value = "com.rongdu")
@ImportResource("classpath:spring.xml")
public class AppConfig {

    @Bean
    @Autowired
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }

    @Bean
    public DataSource dataSource(){
    	//不知道怎么写,就返回空吧
        return null;
    }
}

这里我们通过配置类的方式配置了一个sqlSessionFactotyBean对象,这里的sqlSessionFactotyBean其实就是一个SqlSessionFactoty对象,我们进入这个类可以看到它是实现了FactoryBean这个接口的:

同时我们可以看到,sqlSessionFactotyBean有非常多的属性需要配置,但是我们不用担心,它已经全部帮我们配置好了,我们只需要给它一个数据源就可以了。

所以说其实FactoryBean是比BeanFactory更强大的,这么说可能不太对。回到最前面,我们说FactoryBean是一个bean,现在应该有点理解了吧,我们可以通过继承FactoryBean接口通过getObject方法定义一个bean对象,同时我们可以在xml配置文件或者配置类中定义这个bean的一些属性,最后我们通过ApplicationContext获取配置好的bean对象。

下面我们说下BeanFactory,为什么说它是一个工厂类(接口)?这个其实很好理解,它下面有很多子类用来获取bean对象,如ClassPathXmlApplicationContextAnnotationConfigApplicationContext等,子类通过实现它的getBean方法,返回不同类型的bean对象,这正是工厂模式的体现。

总结一下:

BeanFactory,为什么说它是一个工厂类(接口)?这个其实很好理解,它下面有很多子类用来获取bean对象,如ClassPathXmlApplicationContextAnnotationConfigApplicationContext等,子类通过实现它的getBean方法,返回不同类型的bean对象,这正是工厂模式的体现。

总结一下:

BeanFactory是spring中的一个工厂接口,它能够生产并获取对象,BeanFactory是spring中的一个特殊的bean接口,它有三个方法,其他类通过实现BeanFactory,重写getObject方法返回一个bean对象,如果需要返回当前类对象本身,需要在ApplicationContext.getBean方法加一个&符号,如上面的applicationContext.getBean("&daoFactoryBean")。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值