文章目录
一、FactoryBean
先看FactoryBean的注释
第一段翻译:BeanFactory管理的对象有些会实现这个接口,实现了这个接口的对象用来生产单个对象的工厂。如果一个bean实现了这个接口,他将作为一个工厂来暴露一个新对象,而不是直接作为工厂暴露自己本身。
第二段翻译:实现了这个接口的bean不能当作普通bean使用。一个FactoryBean以普通bean的形式定义,但是总是通过getObject()方法来创建它所包装的bean,并暴露出去。
1.1 getObject
注释翻译:返回被这个工厂管理的实例。如同BeanFactory一样,支持单例和多例模式。调用此方法时如果对象尚未被完整初始化,将会抛出异常
光看它的方法和注释可能还是不明白FactoryBean到底有啥用,咱们来看看源码是怎么对他进行处理的
二、源码中的特殊处理
这块是创建bean的实例(具体创建过程可以看本专栏前几篇文章),调用完getSingleton之后,产生的实例已经加入到容器中。继续进入beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
第一个if判断是否要获取factory本身,第二个判断bean实例是否实现了FactoryBean,进入if就直接返回了。
能走进红框代码说明,当前实例是实现了FactoryBean的,并且getObject产生的对象未被缓存。继续进入
此时外层if的条件是满足的,因为我们的bean都是单例的,而且factory本身也是已经进入了缓存。factoryBeanObjectCache存的是getObject返回的对象,此时肯定是空的,因此内层if也会进入,进去object = doGetObjectFromFactoryBean(factory, beanName);
就在这里调用了getObject(),返回之后在getObjectFromFactoryBean中将返回的对象加入到factoryBeanObjectCache。因此getBean的过程中产生了两个对象,一个factory本身,一个getObject返回的对象,它们存在不同的地方,前者是singletonObjects,后者是factoryBeanObjectCache。
三、使用示例
Spring对FactoryBean做了特殊处理,到底这样做了之后有什么用呢?在另一个MyBatis的专栏中有使用到。在这里模拟手写一下Spring如何从Mybatis缓存中拿到Mapper代理对象,这个过程中就会用到FactoryBean
3.1 MapperScan
自定义一个MapperScan注解,依赖自定义的注册器YexMapperScannerRegistrar
import org.springframework.context.annotation.Import;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(YexMapperScannerRegistrar.class)
public @interface MapperScan {
String value() default "";
}
3.2 YexMapperScannerRegistrar
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
public class YexMapperScannerRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
String path = (String) importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()).get("value");
YexClassPathMapperScanner scanner = new YexClassPathMapperScanner(registry);
scanner.addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
scanner.scan(path);
}
}
ImportBeanDefinitionRegistrar被回调,这块new了一个自定义的BeanDefinition扫描器YexClassPathMapperScanner。它将扫描指定的路径,并将得到的BeanDefinition放入容器。
3.3 YexClassPathMapperScanner
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import java.util.Set;
public class YexClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
public YexClassPathMapperScanner(BeanDefinitionRegistry registry) {
super(registry);
}
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
//使用父类的方法扫描指定的包路径,返回的beanDefinitions 已经在容器当中
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
//处理beanDefinition
processBeanDefinitions(beanDefinitions);
return beanDefinitions;
}
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
AbstractBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (AbstractBeanDefinition) holder.getBeanDefinition();
String beanClassName = definition.getBeanClassName();
//设置bean实例化时使用的构造方法
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
//将definition的类型改为YexMapperFactoryBean,自定义的FactoryBean
definition.setBeanClass(YexMapperFactoryBean.class);
}
}
@Override
public boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isInterface();
}
}
super.doScan(basePackages);扫描得到的BeanDefinition是接口,无法实例化,因此在后面将beanClass设置为YexMapperFactoryBean。那么在实例化的时候就会进行特殊处理(前面有讲到),先产生一个YexMapperFactoryBean对象(名称是“&+beanName”),再产生一个getObject返回的对象(名称就是beanName)。因此,在依赖注入时获得的是getObject返回的对象
3.4 YexMapperFactoryBean
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
public class YexMapperFactoryBean implements FactoryBean {
private SqlSession sqlSession;
private Class mapperInterface;
//实例化时设置属性
public YexMapperFactoryBean(Class mapperInterface) {
this.mapperInterface = mapperInterface;
}
//注入一个Mybatis的SqlSessionFactory,添加mapper接口,该过程会产生代理
@Autowired
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
sqlSessionFactory.getConfiguration().addMapper(mapperInterface);
this.sqlSession = sqlSessionFactory.openSession();
}
//得到Mybatis产生的Mapper代理对象
@Override
public Object getObject() throws Exception {
return sqlSession.getMapper(mapperInterface);
}
@Override
public Class<?> getObjectType() {
return mapperInterface;
}
public void setMapperInterface(Class mapperInterface) {
this.mapperInterface = mapperInterface;
}
}
实例化YexMapperFactoryBean
添加进Mybatis的缓存
得到Mapper代理对象
四、总结
- FactoryBean包装一个对象,使用getObject()方法来获取真正的实例化对象
- FactoryBean包装的对象一般来自第三方,无法添加@Component等注解,就无法直接被Spring管理
- 这些对象是已经被实例化了的,有统一的来源,例如来自Mybatis
- 当然也可以自定义一个bean,使用FactoryBean来包装,但这好像是多余的。@Bean注解可以很好的解决需要自定义实例化、初始化过程的bean