在我们的开发工作中应该都见过或使用过FactoryBean这个类,也许你会看成了BeanFactory这个类。FactoryBean和BeanFactory虽然长的很像,但是他们的作用确实完全不像。这里你可以想象一下,你会在什么样的场景下使用FactoryBean这个接口?
1、二者的定义
1、FactoryBean是一个工厂Bean,可以生成某一个类型Bean实例,它最大的一个作用是:可以让我们自定义Bean的创建过程。FactoryBean本质就是用来给我们实例化、或者动态的注入一些比较复杂的Bean,比如像一些接口的代理对象。
2、BeanFactory是Spring容器中的一个基本类也是很重要的一个类,在BeanFactory中可以创建和管理Spring容器中的Bean,它对于Bean的创建有一个统一的流程
2、二者最主要的一个区别是
1、FactoryBean是动态注入Bean
2、BeanFactory主要是负责Bean实例化、定位、配置应用程序中的对象及建立这些对象间的依赖
3、有什么框架使用了FactoryBean,比如
1、生成AOP代理对象的ProxyFactoryBean
2、生成mapper代理对象的MapperFactoryBean
下面重点介绍FactoryBean源码和使用案例
一、FactoryBean类
看下这个类的结构
public interface FactoryBean<T> {
//返回的对象实例,我们可以在这里返回我们需要实例化的Bean
T getObject() throws Exception;
//Bean的类型,针对上面返回的实例,我们指定实例所对应的类型
Class<?> getObjectType();
//true是单例,false是非单例 在Spring5.0中此方法利用了JDK1.8的新特性变成了default方法,返回true
boolean isSingleton();
}
很重要的三个特性:是否单例、Bean类型、Bean实例,就是没有返回BeanName的值,但是我们也知道BeanName的值。下面我们来写一个关于FactoryBean的小例子,看看我们是怎么使用FactoryBean的,然后再从源码的角度看看Spring是怎么解析FactoryBean中的Bean的。
1、如果一个bean实现了这个接口,那么这个bean将作为一个生成object的工厂来暴漏,而不是直接将这个bean暴漏出去,其实暴漏的是实现方法中getObject()所生成的对象,这个bean是生成object的一个工厂。
2、FactoryBean一般用于框架中,在实际开发中用的少。使用场景一般是,我们创建某个object比较复杂,那就需要一个实现FactoryBean接口的bean作为生成object的工厂注册到spring中。重在理解这个接口。
二、使用案例1
这个案例和我们平时开发中Mapper接口如何被实例化很相似,在获取FactoryBean对象的时候,获取的是getObject() 方法的bean对象,那么我们可以基于FactoryBean设置出一个获取所有Mapper代理对象的bean
public class MyFactoryBean<T> implements FactoryBean<T> {
//我们常用的Mapper类型,是一个接口,这样我们在创建代码的时候,就可以代理来给接口来创建代理对象
private final Class<T> clazz;
public MyFactoryBean(Class<T> clazz) {
this.clazz = clazz;
}
@Override
public T getObject() throws Exception {
return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{clazz}, new MyFactoryBeanHandler());
}
@Override
public Class<?> getObjectType() {
return clazz;
}
}
注意:上面代码中涉及的类MyFactoryBeanHandler,是一个实现了InvocationHandler接口的类,当接口clazz的某个方法被执行时,就会被MyFactoryBeanHandler的invoke拦截
这样我们在getObject()的时候就可以返回我们的代理对象了,到这里就能实例化我们自己定义的的FactoryBean,从而在获取我们的Mapper接口时获取的都是代理对象,这就是Spring整合Mybatis的核心实现。也是FactoryBean的一个应用场景
三、使用案例2
//案例:FactoryBean接口的实现类FactoryBeanLearn
@Component
public class FactoryBeanLearn implements FactoryBean {
@Override
public Object getObject() throws Exception {
//这个Bean是我们自己new的,这里我们自己控制Bean的创建过程了,但是源码中往往都是通过反射来创建,这里就简单搞一下
return new FactoryBeanServiceImpl();
}
@Override
public Class<?> getObjectType() {
return FactoryBeanService.class;
}
@Override
public boolean isSingleton() {
return false;
}
}
//接口
public interface FactoryBeanService {
/**
* 测试FactoryBean
*/
void testFactoryBean();
}
//实现类
public class FactoryBeanServiceImpl implements FactoryBeanService {
/**
* 测试FactoryBean
*/
@Override
public void testFactoryBean() {
System.out.println("我是FactoryBean的一个测试类。。。。");
}
}
@RunWith(SpringRunner.class)
@SpringBootTest
public class Springboot2MybatisDemoApplicationTests {
@Autowired
private ApplicationContext applicationContext;
@Test
public void contextLoads() {
//注意这里是直接拿到FactoryBeanService类型的Bean
FactoryBeanService bean = applicationContext.getBean(FactoryBeanService.class);
bean.testFactoryBean();
}
}
在测试类中,我们是通过Type来获取FactoryBeanServiceImpl类型的Bean,但是我们并没有在FactoryBeanServiceImpl加@Service注解,也没有指定向容器中注册FactoryBeanService类型的Bean,那么applicationContext是怎么获取FactoryBeanService类型的Bean。下面看下源码是怎么实现的
applicationContext.getBean(FactoryBeanService.class);
getBean的重载方法有很多,最终会进到下面resolveBean方法,其中requiredType就是我们需要获取的Bean类型,也就是FactoryBeanService.class
@Nullable
private <T> T resolveBean(ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) {
NamedBeanHolder<T> namedBean = resolveNamedBean(requiredType, args, nonUniqueAsNull);
if (namedBean != null) {
return namedBean.getBeanInstance();
}
。。。。。。
}
resolveNamedBean方法最终会调用resolveNamedBean方法,resolveNamedBean会通过BeanType来找匹配
@Nullable
private <T> NamedBeanHolder<T> resolveNamedBean(ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) throws BeansException {
//很重要的方法:通过Type来匹配,返回类型匹配成功的Bean的Name,为什么需要name,因为最终返回Bean的实例的时候,也是调用通过Name来获取Bean的,也就是getBean(beanName)方法
String[] candidateNames = getBeanNamesForType(requiredType);
if (candidateNames.length == 1) {
//2、匹配到一个Bean的情况,拿到这个Bean的name,调用getBean(beanName)方法来获取Bean的实例
String beanName = candidateNames[0];
return new NamedBeanHolder<>(beanName, (T) getBean(beanName, requiredType.toClass(), args));
} else if (candidateNames.length > 1) {
//3、有多个Bean实例的话 则取带有Primary注解或者带有Primary信息的Bean
String candidateName = determinePrimaryCandidate(candidates, requiredType.toClass());
if (candidateName == null) {
//如果没有Primary注解或者Primary相关的信息,则取优先级高的Bean实例
candidateName = determineHighestPriorityCandidate(candidates, requiredType.toClass());
}
。。。。。。
}
return null;
}
1、getBeanNamesForType方法是通过调用doGetBeanNamesForType方法来得到符合的BeanName集合
2、doGetBeanNamesForType方法就是通过传入的Bean的Class,去和Bean集合中的Type匹配,返回所有匹配成功的Bean的名称,如果
private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
List<String> result = new ArrayList<>();
//拿到容器中所有的Bean,然后遍历,注意这里包含我们自定义的Bean,也就是factoryBeanLearn。
//当BeanName等于factoryBeanLearn,注意不是factoryBeanService,因为容器我们塞得是factoryBeanLearn
for (String beanName : this.beanDefinitionNames) {
//不是别名
if (!isAlias(beanName)) {
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
//Bean不是抽象类、不是延迟初始化的
if (!mbd.isAbstract() && !mbd.isLazyInit() && .....) {
//重点:判断是不是FactoryBean的子类
boolean isFactoryBean = isFactoryBean(beanName, mbd);
BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
boolean matchFound = false;
boolean allowFactoryBeanInit = (allowEagerInit || containsSingleton(beanName));
boolean isNonLazyDecorated = (dbd != null && !mbd.isLazyInit());
if (!isFactoryBean) {
。。。
} else {
//是FactoryBean的子类
if (includeNonSingletons || isNonLazyDecorated || [allowFactoryBeanInit && isSingleton(beanName, mbd, dbd)]) {
//1、第一次通过type匹配Bean,判断这个FactoryBean所产生的BeanType是否是FactoryBeanService.class
matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
}
if (!matchFound) {
//2、如果第一次匹配类型失败,尝试第二次匹配,注意beanName就是FactoryBean,type是factoryBeanService.class
beanName = FACTORY_BEAN_PREFIX + beanName;
matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
}
}
if (matchFound) {
result.add(beanName);
}
}
}
}
//返回匹配成功的Bean的name
return StringUtils.toStringArray(result);
}
isTypeMatch,其实就是调用了FactoryBeanLearn.getObjectType()方法。
//isTypeMatch调用getTypeForFactoryBean方法,简介得到这个FactoryBean产生的Bean的Class,也就是FactoryBeanService
@Nullable
protected Class<?> getTypeForFactoryBean(FactoryBean<?> factoryBean) {
return factoryBean.getObjectType();
}
最终调的就是我们重写的这个方法:
2、再回到上面的代码,当返回BeanName后,再去通过BeanName来获取Bean的实例
if (candidateNames.length == 1) {
String beanName = candidateNames[0];
return new NamedBeanHolder<>(beanName, (T) getBean(beanName, requiredType.toClass(), args));
}
当匹配成功返回Bean的Name=factoryBeanLearn,然后再通过beanName来获取Bean的实例,下面看下针对FactoryBean这种类型的Bean是如何获取实例的。
getBean(beanName, requiredType.toClass(), args)
首先调用getBean()方法->doGetBean()方法
doGetBean()方法最终会调用->getObjectForBeanInstance()方法->getObjectFromFactoryBean()方法
看下doGetObjectFromFactoryBean方法怎么做的
private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {
Object object = factory.getObject();
return object;
}
直接调用我们重写的getObject方法
以上就是通过Type获取我们需要的Bean,包括我们在使用@Autowired注解,默认按照byType自动注入也是类似
四、案例2总结:
1、根据类型FactoryBeanService从Spring容器中获取Bean(首先明确的一点是在Spring容器中没有FactoryBeanService类型的BeanDefinition。但是却有一个Bean和FactoryBeanService这个类型有一些关系)。
2、Spring在根据type去获取Bean的时候,会先获取到beanName。
3、获取beanName的过程是:先循环Spring容器中的所有的beanName,然后根据beanName获取对应的BeanDefinition,如果当前bean是FactoryBean的类型,则会从Spring容器中根据beanName获取对应的Bean实例,接着调用获取到的Bean实例的getObjectType方法获取到Class类型,判断此Class类型和我们传入的Class是否是同一类型。如果是则返回测beanName。
根据factoryBeanLearn获取到FactoryBeanLearn实例,调用FactoryBeanLearn的getObjectType方法获取到返回值FactoryBeanService.class。和我们传入的类型一致,所以这里获取的beanName为factoryBeanLearn。换句话说这里我们把factoryBeanLearn这个beanName映射为了:FactoryBeanService类型。即FactoryBeanService类型对应的beanName为factoryBeanLearn这是很重要的一点。在这里我们也看到了FactoryBean中三个方法中的一个所发挥作用的地方。