前置知识: FactoryBean的作用

FactoryBean

简介

FactoryBean是Spring中一种特殊的Bean,是用于创建Bean对象的,最大的作用便是可以让我们自定义Bean的创建过程。如果你在XML配置文件配置了一个节点,我们通过ApplicationContext获取的对象实际上是getObject方法返回的对象,而不是其本身。FactoryBean的定义如下:

public interface FactoryBean<T> {
    
    /**
     * 自定义创建Bean的方法
     */
    T getObject() throws Exception;
    
    /**
     * Bean的类型
     */
    Class<?> getObjectType();
    
    /**
     * 是不是单例
     */
    boolean isSingleton();
}

使用

想要掌握一个东西,莫过于先使用它。

/**
 * Bean
 */
public class Mapper {

    private Integer id;

    public Mapper(Integer id) {
        this.id = id;
    }

    public Integer getId() {
        return id;
    }
}

public class MapperFactoryBean implements FactoryBean<Mapper> {

    private Integer id;

    private Mapper mapper;

    public void setId(Integer id) {
        this.id = id;
    }

    @Override
    public Mapper getObject() {
        if (mapper == null) {
            mapper = new Mapper(id);
        }
        return mapper;
    }

    @Override
    public Class<?> getObjectType() {
        return Mapper.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}
<!-- 配置 -->
<bean id="mapper" class="com.wangtao.spring.bean.MapperFactoryBean">
    <property name="id" value="1"/>
</bean>
public class BaseTest {
    @Test
    public void application() {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        // 下面这句将抛出异常
        // MapperFactoryBean mapper = context.getBean("mapper", MapperFactoryBean.class);
        Mapper mapper = context.getBean("mapper", Mapper.class);
        Assert.assertEquals(1, mapper.getId().intValue());
        
        // ["mapper"], beanName返回mapper
        context.getBeanNamesForType(Mapper.class);
        // ["&mapper"], beanName会拼接&, 容器中存储的beanName是mapper,但是会根据传入参数判断
        // 如果是FactoryBean,则会拼接&
        context.getBeanNamesForType(MapperFactoryBean.class)
    }
}

从测试结果中得知,我们虽然配置的是MapperFactoryBean的实列,但是根据id拿到的是getObject方法创建的对象。其实在容器中创建的对象仍然是MapperFactoryBean的实列,只是在获取的时候会判断这个结果对象是不是派生于FactoryBean,如果是的话则返回getObject方法创建的对象,并且这个对象并不是容器初始化时创建的,而是使用context.getBean()方法时才创建。当然了,如果你确实想要获取FactoryBean实例,你需要这样写: MapperFactoryBean mapper = context.getBean("&mapper", MapperFactoryBean.class); 只需要在bean的名字ID前加上&符号。

容器初始化时(refresh),在singletonObjects这个map中存储的key是mapper, value是MapperFactoryBean实例。

getBean("beanName")逻辑:

  1. 如果beanName以&开头,会先去掉&前缀,然后从singletonObjects拿到对应的实例对象,因为容器中的key是不会带&的
  2. 如果beanName以&开头,判断第一步获取到的这个实例是不是FactoryBean,如果是直接返回这个实例对象,不是则抛出BeanIsNotAFactoryException
  3. 如果beanName不是以&开头,判断第一步获取到的这个实例是不是FactoryBean,如果不是直接返回,如果是,将FactoryBean的getObject方法返回的对象返回给用户,这里如果FactoryBean的isSingleton方法返回true,还会将结果缓存起来,下一次调用从缓存中返回,因为它是单例的,这样用户在实现FactoryBean的getObject方法时无需保证每次返回的是同一个对象。
posted on 2019-03-04 13:33  wastonl  阅读(1723)  评论(0)    收藏  举报