Spring之FactoryBean

2021-02-25  本文已影响0人  一个菜鸟JAVA

引入

在Spring中存在着BeanFactory和FactoryBean两个接口,很多人容易搞错甚至不知道他们之间的区别。实际上它们的区别特别大,它们俩都不是同一个东西。BeanFactory就是Bean工厂,就是Spring中底层的IOC容器。而FactoryBean是干嘛的呢?

为什么要提供该接口

一般情况下,Spring是通过反射机制利用Bean的Class属性指定实现类来实例化Class。但是在某些情况下,实例化Bean过程比较复杂,传统的方式就不太适合用来实例化Bean。所以Spring提供了FactoryBean这么一个接口,可以通过该接口实现定制实例化Bean的逻辑。接口定义如下:

public interface FactoryBean<T> {
   String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
   /**
   * 返回工厂管理的实例实例对象
   */
   T getObject() throws Exception;
   /**
   * 返回FactoryBean创建的实例的类型
   */
   Class<?> getObjectType();
   /**
   * 是否是单例
   */
   default boolean isSingleton() {
      return true;
   }
}

该定义为Spring-5.2.12.RELEASE版本中的内容。一共就三个接口,作用分别是返回实例对象、类型、是否是单例。

如何使用

上面已经介绍FactoryBean的作用,下面示例展示如何使用。

public class FactoryBeanDemo {
    public static void main(String[] args) {
        //创建IOC容器
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        //读取配置文件
        BeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        reader.loadBeanDefinitions("spring-factory-bean.xml");
        //获取UserService实例
        UserService us1 = (UserService) beanFactory.getBean("userService");
        System.out.println(us1);
        //获取UserServiceFactoryBean实例
        UserServiceFactoryBean usfb = (UserServiceFactoryBean) beanFactory.getBean("&userService");
        System.out.println(usfb);
    }
}
class UserServiceFactoryBean implements FactoryBean<UserService>{
    public UserServiceFactoryBean() {
        System.out.println("调用无参构造函数创建UserServiceFactoryBean:"+this);
    }
    @Override
    public UserService getObject() throws Exception {
        return new UserService();
    }
    @Override
    public Class<?> getObjectType() {
        return UserService.class;
    }
}
class UserService{
    public UserService() {
        System.out.println("调用无参构造函数创建UserService:"+this);
    }
}

spring-factory-bean.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 https://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="userService" class="com.buydeem.factorybean.UserServiceFactoryBean"/>
</beans>

运行后的打印结果如下:

调用无参构造函数创建UserServiceFactoryBean:com.buydeem.factorybean.UserServiceFactoryBean@627551fb
调用无参构造函数创建UserService:com.buydeem.factorybean.UserService@614ddd49
com.buydeem.factorybean.UserService@614ddd49
com.buydeem.factorybean.UserServiceFactoryBean@627551fb

打印的结果你一定很意外,因为我们的配置文件中我们注册的userService它的class为com.buydeem.factorybean.UserServiceFactoryBean,而我们通过getBean("userService")得到的却是UserService类型,而不是UserServiceBean类型。

因为FactoryBean是一个特殊的Bean。我们自定义的UserServiceFactoryBean实现了FactoryBean接口,该类型会在容器中注册两个Bean,其中一个为UserServiceFactoryBean另一个为getObject()方法返回的对象。而且我们通过BeanName获取的为UserService而不是UserServiceFactoryBean,如果想要获取UserServiceFactoryBean,只需要在名字前添加&即可。

在使用Mybatis时我们通常需要创建SqlSessionFactory实例,该实例创建过程较为复杂。而在使用Mybatis与Spring整合时,我们可以使用SqlSessionFactoryBean来创建SqlSessionFactory。

spring中相关源码

AbstractBeanFactory中doGetBean源码如下:

doGetBean.jpg

doGetBean方法内容较多,我只截取了一段。该段代码为创建单例Bean时的主要代码,它首先创建sharedInstance这个实例Bean,接着调用getObjectForBeanInstance获取真正的实例。运行我们之前的示例代码,打断点调试可以知道,sharedInstance实际上就是UserServiceFactoryBean实例,而bean才是UserService实例。

getObjectFromFactoryBean方法详情.png

getObjectFromFactoryBean该方法的核心就是判断之前创建的sharedInstance是不是FactoryBean,如果是则获取真正的bean。

上一篇下一篇

猜你喜欢

热点阅读