【Spring源码:FactoryBean一】终于弄懂FactoryBean是如何自定义bean的创建过程了

引言

我们在日常JavaWeb开发中,应该都见过或使用过FactoryBean这个类,很多人估计都没弄懂它和BeanFactory的区别,亦或者是在面试时,可能都会被问到Spring中BeanFactory和FactoryBean的区别。或许我们很多人都没自定义去扩展使用过FactoryBean,但是可以回顾下在哪些框架中有应用到它?例如:Feign的 FeignClientFactoryBean,mybatis-spring的MapperFactoryBean、SqlSessionFactoryBean 等都是扩展实现了Spring的FactoryBean接口,自定义了Bean的创建过程,通过动态代理为Feign接口、Mapper接口生成代理对象,底层屏蔽了很多复杂的Http连接、sql拼接等重复的业务逻辑,我们开发时无需编写实现类

一般情况下,Spring通过反射机制利用class属性指定实现类实例化Bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在此提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑

  • FactoryBean是一个工厂Bean,可以生成某一类型Bean实例,它最大的一个作用是:可以让我们自定义Bean的创建过程
  • BeanFactory是Spring容器中的一个基本类也是很重要的一个类,在BeanFactory中可以创建和管理Spring容器中的Bean,它对于Bean的创建有一个统一的流程

FactoryBean 接口定义如下:

public interface FactoryBean<T> {

	String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";

    // 这个方法,我们可以自定义返回bean实例
	@Nullable
	T getObject() throws Exception;

    // 返回bean类型,可判断传入的class类型是否一致
	@Nullable
	Class<?> getObjectType();

    // 默认实现:true-单例,flase-原型
	default boolean isSingleton() {
		return true;
	}

}

在进入正题之前,我们先来一点甜点,写一个简单的测试例子,然后再慢慢深入FactoryBean相关的源码,去分析它是如何自定义Bean的创建过程的,因为篇幅有点长,本文先着重分析Class<?> getObjectType() 方法,下一篇再详细分析getObject()。

定义一个订单相关的service接口

public interface IOrderService {

    void saveOrder();

}

service实现类,这里没有添加spring的bean注解:@Service,也无需在xml配置该bean,为了测试如何通过自定义FactoryBean去获取该bean的。

//@Service
public class OrderServiceImpl implements IOrderService {

    @Override
    public void saveOrder() {
        System.out.println("-----创建订单------");
    }
}

自定义FactoryBean实现:OrderFactoryBean,记得添加 @Component注解,由spring ioc容器管理此bean (orderFactoryBean)

/**
 * @description: 自定义FactoryBean实现
 * @author: stwen_gan
 * @date: 
 **/
@Component
public class OrderFactoryBean implements FactoryBean {

    /**
     * 自定义bean的创建过程
     */
    @Override
    public Object getObject() throws Exception {
        System.out.println("-----调用OrderFactoryBean.getObject----");
        // 1、直接new
        IOrderService orderService = new OrderServiceImpl();
        return orderService;

        // 2、通过动态代理,无需实现类,如 Mybatis的xxxMapper接口
//        OrderMapper bean = (OrderMapper) Proxy.newProxyInstance(OrderFactoryBean.class.getClassLoader(), new Class[]{OrderMapper.class}, new InvocationHandler() {
//            @Override
//            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//                // todo
//                System.out.println(method.getName());
//                return null;
//            }
//        });
//        return bean;

    }

    /**
     * 返回bean的class类型
     */
    @Override
    public Class<?> getObjectType() {
        return IOrderService.class;

    }

    /**
     * 是否单例,默认单例
     * @return
     */
    @Override
    public boolean isSingleton() {
        return true;
    }
}

这里为了方便,getObject()方法中直接new 一个OrderServiceImpl 实现类即可,当然通过动态代理生成代理对象也可以,这里不分析,已注释掉,无需关注,可自行测试。

测试类:

/**
 * @description: 测试
 * @author: stwen_gan
 * @date: 
 **/
public class FactoryBeanTest {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext context= new AnnotationConfigApplicationContext(MyConfig.class);

        //测试自定义FactoryBean实现getObject产生bean 
        IOrderService bean = context.getBean(IOrderService.class);
        System.out.println(bean);
        System.out.println("---------------------");

        System.out.println(context.getBean("orderFactoryBean"));// bean是OrderServiceImpl,并不是OrderFactoryBean
        System.out.println(context.getBean("orderFactoryBean"));// 测试是否单例
        System.out.println(context.getBean("orderFactoryBean"));// 测试是否单例
    }
}

运行输出:

可以发现,获取的bean实例都是同一个,因为上面的 isSingleton方法返回true(单例)。

疑问

  1. 这个IOrderService接口和OrderServiceImpl实现类,我们并没有添加任何bean注解(只是普通的接口/类),但我们却能够通过context.getBean方法从IoC容器中获取到,为什么?
  2. context.getBean("orderFactoryBean")获取到的bean对象不是OrderFactoryBean对象本身,而是OrderFactoryBean.getObject方法中返回的对象OrderServiceImpl,它是如何实现的?
  3. 如果我们要获取OrderFactoryBean对象本身,怎么办?

为了弄明白Spring的FactoryBean其中的奥秘,我们就直接从这个getBean方法入手,它是 AbstractApplicationContext 的方法,有很多子类重载了它的方法,如下:

	// 根据Class类型获取bean
    @Override
	public <T> T getBean(Class<T> requiredType) throws BeansException {
		// 检查BeanFactory的激活状态
        assertBeanFactoryActive();
        //getBeanFactory()获取到的是一个DefaultListableBeanFactory的默认实例
		return getBeanFactory().getBean(requiredType);
	}

    // 根据beanName获取bean
	@Override
	public Object getBean(String name) throws BeansException {
		assertBeanFactoryActive();
		return getBeanFactory().getBean(name);
	}

这里,我们只分析getBean(Class<T> requiredType)根据Class类型获取bean的方法,我们传入的是IOrderService.class类型。

其中,getBeanFactory()获取到的是一个DefaultListableBeanFactory的默认实例,所以我们需要去DefaultListableBeanFactory中看getBean这个方法的具体实现:

DefaultListableBeanFactory#getBean(Class<T> requiredType)

	@Override
	public <T> T getBean(Class<T> requiredType) throws BeansException {
		return getBean(requiredType, (Object[]) null);
	}

	@Override
	public <T> T getBean(Class<T> requiredType, @Nullable Object... args) throws BeansException {
		Assert.notNull(requiredType, "Required type must not be null");
        // 根据传入的Class类型、参数等解析bean
		Object resolved = resolveBean(ResolvableType.forRawClass(requiredType), args, false);
		if (resolved == null) {
			throw new NoSuchBeanDefinitionException(requiredType);
		}
		return (T) resolved;
	}

在上面的代码中,我们重点关注的是resolveBean这个方法:

	@Nullable
	private <T> T resolveBean(ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) {
        // 解析bean
		NamedBeanHolder<T> namedBean = resolveNamedBean(requiredType, args, nonUniqueAsNull);
		if (namedBean != null) {
			return namedBean.getBeanInstance();
		}
        //如果当前Spring容器获取不到相应的bean信息,则从父容器中获取
        //SpringMVC是一个很典型的父子容器
		BeanFactory parent = getParentBeanFactory();
		if (parent instanceof DefaultListableBeanFactory) {
			return ((DefaultListableBeanFactory) parent).resolveBean(requiredType, args, nonUniqueAsNull);
		}
		else if (parent != null) {
			ObjectProvider<T> parentProvider = parent.getBeanProvider(requiredType);
			if (args != null) {
				return parentProvider.getObject(args);
			}
			else {
				return (nonUniqueAsNull ? parentProvider.getIfUnique() : parentProvider.getIfAvailable());
			}
		}
		return null;
	}

重点跟进resolveNamedBean方法,如何解析beanName:

	@Nullable
	private <T> NamedBeanHolder<T> resolveNamedBean(
			ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) throws BeansException {

		Assert.notNull(requiredType, "Required type must not be null");
        //这个方法是根据传入的Class类型来获取BeanName,因为一个接口可以有多个实现类的情况(多态),
        //所以这里返回的是一个String数组,这个过程也比较复杂。
        //这里需要注意的是,我们调用getBean方法传入的type为com.example.demo.spring.factorybean.IOrderService类型,但是我们没有在Spring容器中注入IOrderService类型的Bean
        //正常来说我们在这里是获取不到beanName的,但是事实是不是这样呢?看下面我们对getBeanNamesForType的分析
		String[] candidateNames = getBeanNamesForType(requiredType);

        // 如果有多个BeanName,则筛选合适的BeanName
        // 自动注入相关的判断
		if (candidateNames.length > 1) {
            // 自动注入的bean候选者
			List<String> autowireCandidates = new ArrayList<>(candidateNames.length);
			for (String beanName : candidateNames) {
				if (!containsBeanDefinition(beanName) || getBeanDefinition(beanName).isAutowireCandidate()) {
					autowireCandidates.add(beanName);
				}
			}
			if (!autowireCandidates.isEmpty()) {
                // list 转 String 数组,放入候选的beanNames中
				candidateNames = StringUtils.toStringArray(autowireCandidates);
			}
		}

        //如果BeanName只有一个, 我们调用getBean方法来获取Bean实例来放入到NamedBeanHolder中
        //这里getBean是根据beanName,bean的Class类型和参数来获取bean
		if (candidateNames.length == 1) {
			String beanName = candidateNames[0];
			return new NamedBeanHolder<>(beanName, (T) getBean(beanName, requiredType.toClass(), args));
		}
		else if (candidateNames.length > 1) {
            // 将候选bean放入一个map
			Map<String, Object> candidates = new LinkedHashMap<>(candidateNames.length);
			for (String beanName : candidateNames) {
                //从单例池中根据beanName获取该bean对象
				if (containsSingleton(beanName) && args == null) {
                    // 根据beanName获取bean
					Object beanInstance = getBean(beanName);
					candidates.put(beanName, (beanInstance instanceof NullBean ? null : beanInstance));
				}
				else {
                    //单例池中没有获取到该bean,则通过getType方法继续获取Bean实例
					candidates.put(beanName, getType(beanName));
				}
			}
            //有多个Bean实例的话 则取带有Primary注解或者带有Primary信息的Bean
			String candidateName = determinePrimaryCandidate(candidates, requiredType.toClass());
			if (candidateName == null) {
                //如果没有Primary注解或者Primary相关的信息,则取优先级高的Bean实例
				candidateName = determineHighestPriorityCandidate(candidates, requiredType.toClass());
			}
            // 最终获取到一个合适的beanName后,就可以根据Class类型,继续调用getBean方法获取Bean实例
			if (candidateName != null) {
				Object beanInstance = candidates.get(candidateName);
				if (beanInstance == null || beanInstance instanceof Class) {
					beanInstance = getBean(candidateName, requiredType.toClass(), args);
				}
				return new NamedBeanHolder<>(candidateName, (T) beanInstance);
			}
            // 如果都没有获取到,则抛异常
			if (!nonUniqueAsNull) {
				throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet());
			}
		}

		return null;
	}

在上面的方法中我们传入的type是com.example.demo.spring.factorybean.IOrderService接口类型,但是我们并没有在Spring容器中配置该接口或实现类的bean,那么我们是怎么从getBeanNamesForType获取到beanName的呢?
下面,我们对getBeanNamesForType分析:

	@Override
	public String[] getBeanNamesForType(ResolvableType type) {
		return getBeanNamesForType(type, true, true);
	}

	@Override
	public String[] getBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
		Class<?> resolved = type.resolve();
        // type解析不为空
		if (resolved != null && !type.hasGenerics()) {
			return getBeanNamesForType(resolved, includeNonSingletons, allowEagerInit);
		}
		else {
			return doGetBeanNamesForType(type, includeNonSingletons, allowEagerInit);
		}
	}

	@Override
	public String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
		if (!isConfigurationFrozen() || type == null || !allowEagerInit) {
			return doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, allowEagerInit);
		}
        //先从缓存中获取:根据Class类型获取符合的beanNames
		Map<Class<?>, String[]> cache =
				(includeNonSingletons ? this.allBeanNamesByType : this.singletonBeanNamesByType);
		String[] resolvedBeanNames = cache.get(type);
        // 获取到直接返回
		if (resolvedBeanNames != null) {
			return resolvedBeanNames;
		}
        // 获取不到,则调用doGetBeanNamesForType方法获取符合的beanNames
        // 下面会详细分析此方法
		resolvedBeanNames = doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, true);
        //判断我们传入的类能不能被当前类加载加载
		if (ClassUtils.isCacheSafe(type, getBeanClassLoader())) {
            //放入到缓存中,方便下次直接从缓存中获取
            //key:IOrderService,Value:orderFactoryBean
			cache.put(type, resolvedBeanNames);
		}
		return resolvedBeanNames;
	}

如下,通过调试与分析发现,cache缓存中,key就是我们传入的com.example.demo.spring.factorybean.IOrderService的Class,value却是我们自定义实现FactoryBean的OrderFactoryBean对应的beanName--orderFactoryBean(我们有给OrderFactoryBean类添加@Component注解,加入到Spring IoC管理),此beanName并非orderService这也是本文重点,弄懂此问题很关键,它是如何将OrderFactoryBean对应的beanName与IOrderService.class进行关联的?

因此,我们需要分析doGetBeanNamesForType 方法,它是如何通过传入的IOrderService.Class类型却获取到了OrderFactoryBean的beanName:

	private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
		List<String> result = new ArrayList<>();

		// Check all bean definitions.
        //循环检查IoC中所有的beanName,这个是在Spring容器启动解析Bean的时候放入到这个List中的
		for (String beanName : this.beanDefinitionNames) {
			// Only consider bean as eligible if the bean name
			// is not defined as alias for some other bean.
            // 只有当bean名称没有定义为其他bean的别名时,才是符合要求的
			if (!isAlias(beanName)) {
				try {
                    //根据beanName获取RootBeanDefinition
					RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
					// Only check bean definition if it is complete.
                    // 这里检查beanDefinition信息的完整性
					if (!mbd.isAbstract() && (allowEagerInit ||
							(mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()) &&
									!requiresEagerInitForType(mbd.getFactoryBeanName()))) {
                        // 根据beanName与RootBeanDefinition 判断是否FactoryBean的子类
						boolean isFactoryBean = isFactoryBean(beanName, mbd);
						BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();

                        // 类型、beanName等信息是否匹配
						boolean matchFound = false;
						boolean allowFactoryBeanInit = allowEagerInit || containsSingleton(beanName);
						boolean isNonLazyDecorated = dbd != null && !mbd.isLazyInit();
						// 普通bean(非FactoryBean子类)
                        if (!isFactoryBean) {
							if (includeNonSingletons || isSingleton(beanName, mbd, dbd)) {
								matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
							}
						}
                        // FactoryBean 子类
						else  {
							if (includeNonSingletons || isNonLazyDecorated ||
									(allowFactoryBeanInit && isSingleton(beanName, mbd, dbd))) {
                                // 下面会重点分析此isTypeMatch方法
								matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
							}
                            // matchFound=false时
							if (!matchFound) {
								// In case of FactoryBean, try to match FactoryBean instance itself next.
                                //如果不匹配,需要给beanName添加一个统一的前缀&,&beanName 表示这是FactoryBean子类对应的beanName
								beanName = FACTORY_BEAN_PREFIX + beanName;
								matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
							}
						}
                        // matchFound=true,则表示符合,添加此beanName到result返回
						if (matchFound) {
							result.add(beanName);
						}
					}
				}

			}
		}
        // 省略部分无关代码。。。。

		return StringUtils.toStringArray(result);
	}

在上面的方法中,首先循环遍历Spring中所有的beanNames,根据beanName获取对应的RootBeanDefinition,然后根据我们传入的 ResolvableType type (即 IOrderService.class ),判断是否和当前的beanName相匹配:matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit)

当遍历到我们的OrderFactoryBean对应的beanName时,发现它是FactoryBean的子类,就会进入else,然后通过isTypeMatch方法判断它是否和我们传入的IOrderService.class相匹配,如下,调试发现是匹配的,即matchFound=true,最终会添加此beanName到result返回:

其中,beanName = FACTORY_BEAN_PREFIX + beanName; (FACTORY_BEAN_PREFIX=“&”),这里当 matchFound=false时,为啥要给FactoryBean子类对应的beanName添加一个前缀&呢?

--> 为了获取FactoryBean子类对象bean本身时,可以传 context.getBean("&orderFactoryBean"),可自行测试,输出:

com.example.demo.spring.factorybean.OrderFactoryBean@1f0f1111

如下是调试的一个小技巧,添加条件断点:

所以下面我们将分析isTypeMatch方法,它是怎么进行类型匹配判断的,如何将OrderFactoryBean对应的beanNameIOrderService进行关联?

	protected boolean isTypeMatch(String name, ResolvableType typeToMatch, boolean allowFactoryBeanInit)
			throws NoSuchBeanDefinitionException {

        //转换beanName,这里只探讨beanName为orderFactoryBean时isTypeMatch返回的结果
		String beanName = transformedBeanName(name);
		boolean isFactoryDereference = BeanFactoryUtils.isFactoryDereference(name);

        //这里是用AbstractApplicationContext的子类来从Spring容器中获取Bean,
        //获取beanName为orderFactoryBean的Bean实例 (IoC是可以获取到的,因为我们有在OrderFactoryBean类上添加@Component注解)
		Object beanInstance = getSingleton(beanName, false);// beanName:orderFactoryBean-->OrderFactoryBean
		if (beanInstance != null && beanInstance.getClass() != NullBean.class) {
            //判断获取到的OrderFactoryBean是否是FactoryBean的实例
			if (beanInstance instanceof FactoryBean) {
                //这里判断beanName是不是以&开头,这里明显不是(“orderFactoryBean”),这里可以想一下什么情况下会有&开头的Bean,上面也有说明
				if (!isFactoryDereference) {
                    //这里就是从OrderFactoryBean实例中获取type类型,下面会分析一下这个方法
					Class<?> type = getTypeForFactoryBean((FactoryBean<?>) beanInstance);
                    //从OrderFactoryBean中获取到的type类型和我们传入的类型是不是同一种类型,是则直接返回true
					return (type != null && typeToMatch.isAssignableFrom(type));
				}
				else {
					return typeToMatch.isInstance(beanInstance);
				}
			}
        // 省略。。。

其中,Object beanInstance = getSingleton(beanName, false),从Spring容器中获取对应的bean,这里可以思考一个问题:

使用AbstractApplicationContext的子类从Spring容器中获取Bean和使用BeanFactory的子类从容器中获取Bean有什么区别?

前者获取的是beanName对应的bean实例本身(orderFactoryBean:OrderFactoryBean),后者获取到的是我们BeanFactory子类中自定义getObject()方法返回的实例对象(即 orderFactoryBean:OrderServiceImpl)。

下面,接着分析 getTypeForFactoryBean

	@Nullable
	protected Class<?> getTypeForFactoryBean(final FactoryBean<?> factoryBean) {
		try {
			if (System.getSecurityManager() != null) {
				return AccessController.doPrivileged((PrivilegedAction<Class<?>>)
						factoryBean::getObjectType, getAccessControlContext());
			}
			else {
                //看到这是不是很熟悉了,实际调用FactoryBean实例的getObjectType()方法
				return factoryBean.getObjectType();
			}
		}
        // 省略。。。
	}

通过上面的层层分析,终于发现调用了我们自定义的OrderFactoryBeangetObjectType方法,获取到的值为:com.example.demo.spring.factorybean.IOrderService,和我们传入的type是一样的类型。所以这里返回true,根据我们的分析:如果isTypeMatch返回true的话,我们返回的beanName为orderFactoryBean。因此,我们终于明了:我们调用 context.getBean(IOrderService.class),却将OrderFactoryBean对应的beanNameIOrderService进行关联了。

总结

我们并没有将IOrderService/OrderServiceImpl 配置成Spring bean,即在Spring容器中没有它们对应的BeanDefinition,按理通过IOrderService.class是获取不到对应的bean的,但是我们却自定义了一个工厂Bean(OrderFactoryBean)和IOrderService这个类型有关联,如下:

我们在通过  IOrderService bean = context.getBean(IOrderService.class) 根据type获取bean时,底层Spring会先去获取对应的beanName:

先循环Spring容器中所有的beanNames,然后根据beanName获取对应的BeanDefinition,如果当前bean是FactoryBean的类型,则会从Spring容器中根据beanName获取对应的Bean实例,接着调用获取到的Bean实例的getObjectType方法获取到Class类型,判断此Class类型和我们传入的Class是否是同一类型。如果是则返回此beanName,对应到我们例子就是:根据orderFactoryBean获取到OrderFactoryBean实例,调用OrderFactoryBean的getObjectType方法获取到返回值IOrderService.class。和我们传入的类型一致,所以这里获取的beanName为orderFactoryBean。换句话说这里我们把orderFactoryBean这个beanName映射为了:IOrderService类型IOrderService类型对应的beanName为orderFactoryBean。通过本文弄懂了Factory三个方法中的getObjectType的作用,下一篇文章将分析:getBean(String name, Class requiredType, Object… args)这个方法,FactoryBean最终是如何返回我们自定义的bean的。

最后,我画了一个简单版的FactoryBean流程图,描述了我们传入的class类型(IOrderService.calss)与我们自定义的FactoryBean子类之间如何关联起来的,即我们调用getBean(IOrderService.class),底层最终会调用factoryBean.getObjectType(),方便我们从整体上理清脉络(可点击放大图片):

参考:https://blog.csdn.net/zknxx/article/details/79572387

史上最强Tomcat8性能优化

阿里巴巴为什么能抗住90秒100亿?--服务端高并发分布式架构演进之路

B2B电商平台--ChinaPay银联电子支付功能

学会Zookeeper分布式锁,让面试官对你刮目相看

SpringCloud电商秒杀微服务-Redisson分布式锁方案

查看更多好文,进入公众号--撩我--往期精彩

一只 有深度 有灵魂 的公众号0.0

猜你喜欢

转载自blog.csdn.net/a1036645146/article/details/111661211
今日推荐