在我们的开发工作中应该都见过或使用过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();
}

最终调的就是我们重写的这个方法:



springboot 3 DubboReference 工具类_FactoryBean


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()方法



springboot 3 DubboReference 工具类_区别_02


doGetBean()方法最终会调用->getObjectForBeanInstance()方法->getObjectFromFactoryBean()方法



springboot 3 DubboReference 工具类_FactoryBean_03


看下doGetObjectFromFactoryBean方法怎么做的

private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {
	Object object = factory.getObject();
	return object;
}

直接调用我们重写的getObject方法



springboot 3 DubboReference 工具类_区别_04


以上就是通过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中三个方法中的一个所发挥作用的地方。