美文网首页
Spring之FactoryBean

Spring之FactoryBean

作者: 一个菜鸟JAVA | 来源:发表于2021-02-25 17:19 被阅读0次

    引入

    在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。

    相关文章

      网友评论

          本文标题:Spring之FactoryBean

          本文链接:https://www.haomeiwen.com/subject/zquufltx.html