实例区别BeanFactory和FactoryBean

前言

从代码角度写了一个帖子去解释BeanFactory和FactoryBean,感概下Mybatis对Spring框架用的炉火纯青的水平。更多Spring内容进入【Spring解读系列目录】

BeanFactory

什么是BeanFactory?看名字也能明白这是一个工厂,其实是Spring框架中的一个顶层类。是Spring提供的一个工厂,能够产生注册在Spring中的对象。比如BeanFactory.getBean(),就可以拿到一个bean的实例对象。或者实例化AnnotationConfigApplicationContext类就可以拿到其对象,也就是说BeanFactory是为了产生对象使用的。这个其实没什么好讲的,重点就在于FactoryBean

BeanFactory bean=new AnnotationConfigApplicationContext();
bean.getBean("service");

FactoryBean

FactoryBean是Spring中的一个特殊的bean。什么是bean呢?其实只要一个类交给Spring容器管理就可以称作一个Bean,也可以直接说就是一个对象。那FactoryBean有什么特殊性呢?那就是FactoryBean可以构造一个新的bean出来。我们先用一个例子来简单说下,先创建一个测试类TestDaoFactoryBean,然后创建一个MyDaoFactroyBean类实现FactoryBean,再实现里面的方法,起个名字叫factoryBeanTest

public class TestDaoFactoryBean {
    
     }
@Component("factoryBeanTest")
public class MyFactroyBean implements FactoryBean {
    
    
    @Override
    public Object getObject() throws Exception {
    
    
        return new TestDaoFactoryBean(); //getObject返回新构建的类
    }
    @Override
    public Class<?> getObjectType() {
    
    
        return TestDaoFactoryBean.class;
    }
    @Override
    public boolean isSingleton() {
    
      //设置单例模式
        return true;
    }
}

构建完成以后我们就尝试去把MyFactroyBean拿出来,所以我们用一个Test类取一下:

public class Test {
    
    
    public static void main(String[] args) {
    
    
        AnnotationConfigApplicationContext anno=new AnnotationConfigApplicationContext(AppConfig.class);
        System.out.println(anno.getBean("factoryBeanTest"));
    }
}
打印结果:
com.demo.dao.TestDaoFactoryBean@701fc37a

最终运行的结果发现取出来的并不是MyFactroyBean,而是我们在getObject()这个方法里返回的对象。那么怎么拿到MyFactroyBean呢,只要把名字前面加一个”&”符号就可以了

System.out.println(anno.getBean("&factoryBeanTest"));
打印结果:
com.demo.dao.MyFactroyBean@701fc37a

为什么会这样呢?先不解释,不过根据这个现象我们总结一下:
如果一个类实现了FactoryBean,那么Spring容器中就会生成两个对象,一个是getObject()方法返回的对象,还有一个是当前的对象(也就是this本身)。getObject()对象存的是指定的对象,在Bean上指定名字的对象。当前对象是存的是”&”+当前类的名字

FactoryBean的作用

要想解释为什么Spring要这样设计,首先我们看先看FactoryBean有什么具体的作用。做一个假设,我们需要引用第三方的一个jar包,这里面有一个配置类A,而这个配置类A又依赖了其他100个类。那么请问,这个其他的100个类的依赖,应该由谁去管理呢?不管由谁管理,一定不是让使用者管理对不对,我们期望的是在Spring中配置了一个A的bean,就可以正常使用这个jar包了,而不是说要使用者把A中所有的依赖都维护一遍,这显然不合理。比如我们经常使用的Mybatis就是一个非常经典的例子。

一般来说都会把Mybatis交给Spring管理,如果Mybatis里面使用的第三方的类也都需要开发人员去维护是不是极为不合理。所以我们期望就是Mybatis自己维护好自己的关系,然后提供一个bean(对象)出来,我只要维护这个bean就可以完成Spring的管理了。以Mybatis中的SqlSessionFactory为例。如果要我们自己配Spring依赖关系,首先要配一个SqlSessionFactorybean,然后发现里面依赖了Configuration对象,又要配置一堆依赖关系,配完以后Configuration对象又依赖了Environment对象和好几十个其他对象,要一个一个配bean出来,单单交给Spring管理那就是一个相当巨大的工程了,还不如自己写来的简单。所以最好的办法就是Mybatis自己把这一切的关系都维护好了,使用者只要引用SqlSessionFactory一个bean就搞定了,这个时候FactoryBean就有大作用了。Mybatis也提供了这样一个bean叫做SqlSessionFactoryBean就做了这样一个事情。

#Mybatis提供的FactorBean:
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
    
    
	...
	private SqlSessionFactory sqlSessionFactory;
	...
	@Override
	public SqlSessionFactory getObject() throws Exception {
    
    
	  if (this.sqlSessionFactory == null) {
    
    
 	   afterPropertiesSet();
	  }
 	 return this.sqlSessionFactory;
	}
	...
}

这个类也实现了FactoryBean,虽然叫做SqlSessionFactoryBean但是它最终返回的是SqlSessionFactory。在SqlSessionFactoryBean中Mybatis就把ConfigurationEnvironment等等这些对象提前初始化,并且用setter方法赋值,那么最终我们拿到的bean就是已经初始化好了对象直接可以用。

FactoryBean使用范例

所以为了加深理解,我们也可以做一个例子,首先我们改造一下TestDaoFactoryBean让其具备一些属性。

public class TestDaoFactoryBean {
    
    
    private String a1;
    private Integer a2;
    public String getA1() {
    
    
        return a1;
    }
    public void setA1(String a1) {
    
    
        this.a1 = a1;
    }
    public Integer getA2() {
    
    
        return a2;
    }
    public void setA2(Integer a2) {
    
    
        this.a2 = a2;
    }
}

然后修改MyFactroyBean,并且在里面初始化TestDaoFactoryBean

@Component("factoryBeanTest")
public class MyFactroyBean implements FactoryBean {
    
    
    @Override
    public Object getObject() throws Exception {
    
    
        TestDaoFactoryBean factoryBean=new TestDaoFactoryBean();
        factoryBean.setA1("a1");
        factoryBean.setA2(22222);
        return factoryBean;
    }
    @Override
    public Class<?> getObjectType() {
    
    
        return TestDaoFactoryBean.class;
    }
    @Override
    public boolean isSingleton() {
    
    
        return true;
    }
}

最后获取一下,这样我们就完成了TestDaoFactoryBean的初始化赋值,外部如果要使用就会发现其中的属性a1a2已经被构建过了。

public class Test {
    
    
    public static void main(String[] args) {
    
    
        AnnotationConfigApplicationContext anno=new AnnotationConfigApplicationContext(AppConfig.class);
        TestDaoFactoryBean factoryBean= (TestDaoFactoryBean) anno.getBean("factoryBeanTest");
        System.out.println("getA1:"+factoryBean.getA1());
        System.out.println("getA2:"+factoryBean.getA2());
    }
}
打印结果:
getA1:a1
getA2:22222

但是要注意,实现了FactoryBean的本身是和别的bean没有区别的,唯一的区别就是返回的对象要指定,因此这些实现了FactoryBeanbean也可以进行业务实现。当然这点要根据具体的业务具体去分析。

总结

举个不恰当的例子BeanFactory是个鞋厂,FactoryBean就是一个特殊的鞋子,这个鞋子能够根据需求产生新的鞋子,这么牛逼的鞋子,就问你服不服。从名字上看BeanFactorybean工厂只能生产bean。而FactoryBean是一个bean (对象),但是这个bean能够生产新的bean。好绕,大家理解下。

那么如果有人问道这两个的区别,大概可以这样说:BeanFactory是Spring容器当中的工厂,它能生产我们交给Spring的类,并且获取这些类的对象。FactoryBean只是Spring中的一个特殊的bean接口,FactoryBean需要被实现三个方法,实现以后可以返回一个新的bean,这个新的bean就是getObject()返回出去的对象。并且存在的形式也不同,得到FactoryBean本身需要用&+类名,而要得到FactoryBean产生的对象则要使用真实指定的bean名字。

猜你喜欢

转载自blog.csdn.net/Smallc0de/article/details/108348049