前置知识: FactoryBean的作用
FactoryBean
简介
FactoryBean是Spring中一种特殊的Bean,是用于创建Bean对象的,最大的作用便是可以让我们自定义Bean的创建过程。如果你在XML配置文件配置了一个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")逻辑:
- 如果beanName以&开头,会先去掉&前缀,然后从singletonObjects拿到对应的实例对象,因为容器中的key是不会带&的
- 如果beanName以&开头,判断第一步获取到的这个实例是不是FactoryBean,如果是直接返回这个实例对象,不是则抛出BeanIsNotAFactoryException
- 如果beanName不是以&开头,判断第一步获取到的这个实例是不是FactoryBean,如果不是直接返回,如果是,将FactoryBean的getObject方法返回的对象返回给用户,这里如果FactoryBean的isSingleton方法返回true,还会将结果缓存起来,下一次调用从缓存中返回,因为它是单例的,这样用户在实现FactoryBean的getObject方法时无需保证每次返回的是同一个对象。
浙公网安备 33010602011771号