[Spring]Spring的getBean路线-createBean

374 阅读7分钟

前言

承上启下:

  1. Spring的refresh->finishBeanFactoryInitialization(容器初始化单例非延迟加载Bean的入口).
  2. beanFactory.preInstantiateSingletons();,Bean工厂开始加载Bean实例.
  3. 获取beanName列表,循环对每个beanName进行getBean操作.
  4. getBean中调用了doGetBean进行了一些创建Bean的前置处理.最终在getSingleton中调用了createBean进行Bean的创建.

OK,下面进入今天的主题-createBean.

createBean的总体流程

  • 解析Bean类型,获取Class: resolveBeanClass.
  • 检查和准备Bean中的方法覆盖: prepareMethodOverrides.
  • 应用实例化之前的后处理器: resolveBeforeInstantiation.
  • 开始创建Bean: doCreateBean

1. resolveBeanClass-解析Bean类型,获取Bean的Class.

  • AbstractAutowireCapableBeanFactory#createBean
	protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

		if (logger.isTraceEnabled()) {
			logger.trace("Creating instance of bean '" + beanName + "'");
		}
		RootBeanDefinition mbdToUse = mbd;

		// 确认当前的Bean类是可以被解析的,并且支持克隆BeanDefinition,merged的BeanDefinition不在此方法的考虑范围
		// 进入到里面,会发现器通过了类加载器进行了Class的获取
		Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
		if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
			// 克隆一份BeanDefinition,用来设置上加载出来的对象
			// 采用副本的原因是某些类可能需要动态加载Class
			mbdToUse = new RootBeanDefinition(mbd);
			mbdToUse.setBeanClass(resolvedClass);
		}
		// 省略若干代码
}

容器在前面已经加载并注册好了BeanDefinition,它描述了关于Bean的信息,在此处Spring要开始将自己抽象出来的BeanDefinition进行解析.这里思考一个问题,为什么Spring在加载Bean的时候选择了RootBeanDefinition进行解析,而不是之前在加载BeanDefinition的时候反复说到的GenericBeanDefinition?

RootBeanDefinition对应的是Java中的继承体系而制造的复合BeanDefinition(Spring在定义Bean时,也支持定义继承关系).从各种容器加载出来的BeanDefinition,最终都会通过getMergedLocalBeanDefinition转化成RootBeanDefinition.

RootBeanDefinition可以合成有继承关系的BeanDefinition(如果没有继承关系,那么自己便是代表parent).

这里推荐一篇文章,希望可以带来更多的思考: 【小家Spring】Spring IoC容器中核心定义之------BeanDefinition深入分析(RootBeanDefinition、ChildBeanDefinition...)

OK,回到主题,拿到传进来的RootBeanDefinition后,Spring需要确认当前的Bean类是可以被解析的,即是否能通过类加载器进行载入.对于注解容器来说,在组件扫描的时候就通过ResourceLoader进行了资源的定位,所以在这里是可以确认可以被解析的.
考虑到健全性,Spring还是谨慎地进行了校验,毕竟不是所有的BeanDefinition都是通过注解容器加载的.

  • AbstractBeanFactory#resolveBeanClass
	protected Class<?> resolveBeanClass(RootBeanDefinition mbd, String beanName, Class<?>... typesToMatch)
			throws CannotLoadBeanClassException {

		try {
			// 如果当前BeanDefinition存储beanClass属于Class实例,直接返回
			if (mbd.hasBeanClass()) {
				return mbd.getBeanClass();
			}
			if (System.getSecurityManager() != null) {
				return AccessController.doPrivileged((PrivilegedExceptionAction<Class<?>>)
						() -> doResolveBeanClass(mbd, typesToMatch), getAccessControlContext());
			}
			else {
				// 如果当前BeanDefinition没有存储Class实例,那么尝试解析
				return doResolveBeanClass(mbd, typesToMatch);
			}
		}
		catch (PrivilegedActionException pae) {
			ClassNotFoundException ex = (ClassNotFoundException) pae.getException();
			throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), ex);
		}
		catch (ClassNotFoundException ex) {
			throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), ex);
		}
		catch (LinkageError err) {
			throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), err);
		}
	}
  • AbstractBeanFactory#doResolveBeanClass
	private Class<?> doResolveBeanClass(RootBeanDefinition mbd, Class<?>... typesToMatch)
			throws ClassNotFoundException {

		ClassLoader beanClassLoader = getBeanClassLoader();
		ClassLoader dynamicLoader = beanClassLoader;
		boolean freshResolve = false;

		if (!ObjectUtils.isEmpty(typesToMatch)) {
			// When just doing type checks (i.e. not creating an actual instance yet),
			// use the specified temporary class loader (e.g. in a weaving scenario).
			ClassLoader tempClassLoader = getTempClassLoader();
			if (tempClassLoader != null) {
				dynamicLoader = tempClassLoader;
				freshResolve = true;
				if (tempClassLoader instanceof DecoratingClassLoader) {
					DecoratingClassLoader dcl = (DecoratingClassLoader) tempClassLoader;
					for (Class<?> typeToMatch : typesToMatch) {
						dcl.excludeClass(typeToMatch.getName());
					}
				}
			}
		}
		// 获取BeanDefinition的className.
		String className = mbd.getBeanClassName();
		if (className != null) {
			// 根据className解析成表达式
			Object evaluated = evaluateBeanDefinitionString(className, mbd);
			if (!className.equals(evaluated)) {
				// A dynamically resolved expression, supported as of 4.2...
				if (evaluated instanceof Class) {
					return (Class<?>) evaluated;
				}
				else if (evaluated instanceof String) {
					className = (String) evaluated;
					freshResolve = true;
				}
				else {
					throw new IllegalStateException("Invalid class name expression result: " + evaluated);
				}
			}
			if (freshResolve) {
				// When resolving against a temporary class loader, exit early in order
				// to avoid storing the resolved Class in the bean definition.
				if (dynamicLoader != null) {
					try {
						// 如果解析后的表达式为Class则直接返回
						return dynamicLoader.loadClass(className);
					}
					catch (ClassNotFoundException ex) {
						if (logger.isTraceEnabled()) {
							logger.trace("Could not load class [" + className + "] from " + dynamicLoader + ": " + ex);
						}
					}
				}
				// 反射工具类加载Class
				return ClassUtils.forName(className, dynamicLoader);
			}
		}

		// Resolve regularly, caching the result in the BeanDefinition...
		return mbd.resolveBeanClass(beanClassLoader);
	}

这里Spring会获取到当前工厂的类加载器,然后从BeanDefinition中获取className.Spring会先对className做表达式解析,如果不是通过表达式进行解析,那么调用ClassUtils.forName(className, dynamicLoader);,如果className不存在,再使用类加载器进行加载.类于类加载器是一一对应的关系,Spring需要找到可以加载该类的类加载器,然后加载出Class对象.

2. 检查和准备Bean中的方法覆盖-prepareMethodOverrides

  • AbstractAutowireCapableBeanFactory#createBean
		// Prepare method overrides.
		// 检查和准备Bean中的方法覆盖
		try {
			mbdToUse.prepareMethodOverrides();
		}
  • AbstractBeanDefinition#prepareMethodOverrides

主要针对lookup methods做校验和方法覆盖。现在用得比较少了.
如果对Lookup方法注入有兴趣,可以在Spring的官网查看: 点我前往

  • AbstractBeanDefinition#prepareMethodOverrides
	public void prepareMethodOverrides() throws BeanDefinitionValidationException {
		// Check that lookup methods exist and determine their overloaded status.
		// 检查查找方法是否存在,并确定其重载状态。
		if (hasMethodOverrides()) {
			getMethodOverrides().getOverrides().forEach(this::prepareMethodOverride);
		}
	}
  • AbstractBeanDefinition#prepareMethodOverride

验证并准备给定的方法是否被重写。检查是否存在具有指定名称的方法,如果找不到该方法,则将其标记为未重载。

	protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException {
		int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName());
		if (count == 0) {
			throw new BeanDefinitionValidationException(
					"Invalid method override: no method with name '" + mo.getMethodName() +
					"' on class [" + getBeanClassName() + "]");
		}
		// 如果不存在重载,在使用CGLIB增强阶段就不需要校验了
		else if (count == 1) {
			// Mark override as not overloaded, to avoid the overhead of arg type checking.
			// 将替代标记为未过载,以避免arg类型检查的开销。
			mo.setOverloaded(false);
		}
	}

3. 激活Bean实例化前的后置处理器-resolveBeforeInstantiation

		try {
			// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
			// 解析BeanPostProcessors的BeanPostProcessorsBeforeInstantiation,试图返回一个需要创建Bean的代理对象
			// resolveBeforeInstantiation只针对有自定义的targetsource.
			// 因为自定义的targetsource不是Spring的bean,那么后续的初始化与该bean无关
			// 因此直接可以进行代理
			Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
			if (bean != null) {
				return bean;
			}
		}
		catch (Throwable ex) {
			throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
					"BeanPostProcessor before instantiation of bean failed", ex);
		}
  • AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation
	protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
		Object bean = null;
		// 如果beforeInstantiationResolved为false
		if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
			// Make sure bean class is actually resolved at this point.
			// 如果注册了InstantiationAwareBeanPostProcessors的BeanPostProcessor.
			if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
				Class<?> targetType = determineTargetType(beanName, mbd);
				if (targetType != null) {
					// 执行实现了该后置处理器接口的类中postProcessBeforeInitialization方法
					// Spring在这里使用了责任链模式,如果有一个后置处理器可以返回Object,就会马上跳出循环
					// 同时,如果第三方框架在此后置处理器对Bean执行了操作,那么将不会进入到doCreateBean中了.
					// 注意传参,这里传入的是一个class和beanName.
					bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
					if (bean != null) {
						bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
					}
				}
			}
			mbd.beforeInstantiationResolved = (bean != null);
		}
		return bean;
	}

如果当前的Bean实现了InstantiationAwareBeanPostProcessor接口,并且在postProcessBeforeInstantiation中返回了Object,那么Spring将不会执行doCreateBean进行实例化,而是将当前用户返回的bean实例直接返回。
AbstractAutoProxyCreator这个AOP代理的BeanPostProcessor便是这样做的.
执行完before的操作后,如果返回了实例,那么继续执行after的操作.
其中,before和after的执行逻辑有点区别:
before方法执行的中断条件:

				Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
				if (result != null) {
					return result;
				}

after方法执行的中断条件:

			Object current = processor.postProcessAfterInitialization(result, beanName);
			if (current == null) {
				return result;
			}

关注这个BeanPostPorcessor,在后续的学习中,我们仍能在过滤器中学习到这种职责链的设计原则.
我们来看看InstantiationAwareBeanPostProcessor的接口清单.默认返回null.

public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {

	@Nullable
	default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
		return null;
	}

	default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
		return true;
	}
}

4. 开始创建Bean-doCreateBean

OK,再梳理一下思路,从createBean中,Spring分析了当前的Class是否能被类加载器加载,如果可以加载,返回当前BeanDefinitino的Class对象,接着进行了Lookup method的检查,最后激活了InstantiationAwareBeanPostProcessors的方法,如果以上方法都没中断,那么Spring将进行Bean的实例化-doCreateBean.

		try {
			// 创建bean的入口
			Object beanInstance = doCreateBean(beanName, mbdToUse, args);
			if (logger.isTraceEnabled()) {
				logger.trace("Finished creating instance of bean '" + beanName + "'");
			}
			return beanInstance;
		}
		catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
			// A previously detected exception with proper bean creation context already,
			// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
		}

总结

  • 在createBean阶段,Spring会分析当前BeanDefinition的Class是否是可解析的.具体表现为用类加载器进行loadClass或者Class.forName
  • 检查当前BeanDefinition的Lookup method是否存在,并且确认重载状态.
  • 如果实现了InstantiationAwareBeanPostProcessors,激活此后置处理器,如果可以从此后置处理器中获取Bean实例,那么直接返回该Bean实例,不会进行doCreateBean操作.
  • 开始Bean的实例化-doCreateBean.

doCreateBean篇幅较长,将拆分几篇文章进行讲解,如果觉得不错,请关注我的博客~