面试中经常被问到这问题,事实上,很多人只会死记硬背记答案。百度一下这个问题,总结出来的答案大致如下:
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对象,如ClassPathXmlApplicationContext
、AnnotationConfigApplicationContext
等,子类通过实现它的getBean
方法,返回不同类型的bean对象,这正是工厂模式的体现。
总结一下:
BeanFactory
,为什么说它是一个工厂类(接口)?这个其实很好理解,它下面有很多子类用来获取bean对象,如ClassPathXmlApplicationContext
、AnnotationConfigApplicationContext
等,子类通过实现它的getBean
方法,返回不同类型的bean对象,这正是工厂模式的体现。
总结一下:
BeanFactory是spring中的一个工厂接口,它能够生产并获取对象,BeanFactory是spring中的一个特殊的bean接口,它有三个方法,其他类通过实现BeanFactory,重写getObject方法返回一个bean对象,如果需要返回当前类对象本身,需要在ApplicationContext.getBean方法加一个&符号,如上面的applicationContext.getBean("&daoFactoryBean")。