Spring 源码分析衍生篇八 :ConfigurationClassPostProcessor 下篇

一、前言

本文是 Spring源码分析:Spring源码分析二:BeanFactoryPostProcessor 的处理 的衍生文章。主要是因为本人菜鸡,在分析源码的过程中还有一些其他的内容不理解,故开设衍生篇来完善内容以学习。


ConfigurationClassPostProcessor 的分析受篇幅所限,分为上下两篇
上篇 分析 postProcessBeanDefinitionRegistry 方法的调用。
下篇 分析 postProcessBeanFactory 方法的调用。


ConfigurationClassPostProcessor 是非常重要的一个 后处理器。 ConfigurationClassPostProcessor 完成了 配置类的解析和保存。将所有需要注入的bean解析成 BeanDefinition保存到 BeanFactory 中。

1. ConfigurationClassPostProcessor

首先来讲解一下 ConfigurationClassPostProcessor 的结构图如下。
在这里插入图片描述

可见ConfigurationClassPostProcessor 接口实现了BeanDefinitionRegistryPostProcessor(BeanFactory 的后处理器)
PriorityOrdered(设置自己的优先级为最高) 和各种 Aware 接口。

我们这里重点看的是 BeanDefinitionRegistryPostProcessor 接口的两个方法:

// 完成对 @Bean 方法的代理
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
// 允许在Spring容器启动后,在下一个阶段开始前,添加BeanDefinition的定义
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

关于这两个方法的调用时机和作用,我们在之前的文章已经讲过,这里不再赘述。


上篇 分析了 ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry 方法。得知了ConfigurationClassPostProcessor 解析配置类(这里的配置类不仅仅局限于@Configuration 注解,还包括 @Import@ImportResource 等注解),将解析到的需要注入到Spring容器中的bean的BeanDefinition保存起来。在后面的bean 初始化都需要BeanDefinition。

本篇需要分析 ConfigurationClassPostProcessor#postProcessBeanFactory 方法通过cglib代理配置类,来拦截 @Bean修饰的方法。这么做的目的是为了在配置类中多次调用 @Bean 方法返回的是同一个结果。即在下面的代码中 demoController()demoController2() 方法中调用的demoService() 方法返回的结果是同一个值。避免了单例模式下的多例创建。我们可以通过下面一个例子来看一看

二、举例

@Configuration
public class DemoConfig {
    @Bean
    public DemoService demoService(){
        return new DemoServiceImpl();
    }
    @Bean
    public DemoController demoController(){
        System.out.println("demoController : " +  demoService());
        return new DemoController();
    }
    @Bean("demoController2")
    public DemoController demoController2(){
        System.out.println("demoController2222 : " +  demoService());
        return new DemoController();
    }
}

上面的代码输出结果是什么?
在这里插入图片描述

我们看到两个方法里调用 demoService() 方法返回的是同一个实例,但是按照我们传统的逻辑,这里调用 demoService() 应该是重新创建了 一个 DemoServiceImpl 实例,应该不一样的。这里就是因为ConfigurationClassPostProcessor#postProcessBeanFactory 方法通过代理实现了该效果,以保证正确语义。

PS: 如果使用 @Component 注解修饰 DemoConfig 。则两次 demoService() 方法返回的结果则不相同。,因为被 @Component 注解修饰的bean并不会调用 ConfigurationClassPostProcessor#postProcessBeanFactory 方法来进行方法代理。

具体原因,即使因为在 postProcessBeanFactory 方法中对 Full 类型(即被 @Configuration 修饰的配置类)的配置类进行了动态代理。

三、 代码分析

postProcessBeanFactory 方法代码如下(相较于 postProcessBeanDefinitionRegistry 方法真是简单太多了):

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		int factoryId = System.identityHashCode(beanFactory);
		// 判断是否已经在处理
		if (this.factoriesPostProcessed.contains(factoryId)) {
			throw new IllegalStateException(
					"postProcessBeanFactory already called on this post-processor against " + beanFactory);
		}
		this.factoriesPostProcessed.add(factoryId);
		if (!this.registriesPostProcessed.contains(factoryId)) {
			// BeanDefinitionRegistryPostProcessor hook apparently not supported...
			// Simply call processConfigurationClasses lazily at this point then.
			processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
		}

		enhanceConfigurationClasses(beanFactory);
		beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
	}

可以看到关键代码为 enhanceConfigurationClasses(beanFactory);。下面开始就来看看enhanceConfigurationClasses 方法

1. enhanceConfigurationClasses

enhanceConfigurationClasses 方法用于增强配置类。Spring会对 Full Configuration (即被 @Configuration 修饰的配置类)进行代理,拦截@Bean方法,以确保正确处理@Bean语义。这个增强的代理类就是在enhanceConfigurationClasses(beanFactory)方法中产生的。

由于篇幅所限,这里等后续有机会再详细解析。这一部分的解析可以参考 : https://segmentfault.com/a/1190000020633405?utm_source=tag-newest

public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
		Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
		for (String beanName : beanFactory.getBeanDefinitionNames()) {
			BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
			// 获取 CONFIGURATION_CLASS_ATTRIBUTE属性,如果不为null,则是配置类,在上篇中有过交代(可能是full或者lite类型)
			Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
			MethodMetadata methodMetadata = null;
			if (beanDef instanceof AnnotatedBeanDefinition) {
				methodMetadata = ((AnnotatedBeanDefinition) beanDef).getFactoryMethodMetadata();
			}
			// 如果是配置类(configClassAttr  != null) || @Bean注解派生的方法(methodMetadata != null 不为空表示 FactoryMethod不为空,则可以说明是 @Bean 生成的 BeanDefinition)
			if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {
				// Configuration class (full or lite) or a configuration-derived @Bean method
				// -> resolve bean class at this point...
				AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDef;
				// 这里判断如果指定的 bean(注意并非这里的abd,而是abd所要生成的bean) 如果不是 Class类型则进入 if里面
				if (!abd.hasBeanClass()) {
					try {
						// 解析 beanClass,即获取这个 Bean 的Class 并保存到 abd中
						abd.resolveBeanClass(this.beanClassLoader);
					}
					catch (Throwable ex) {
						throw new IllegalStateException(
								"Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
					}
				}
			}
			// 对 FUll的 配置类进行处理!!!
			if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
				// 对非AbstractBeanDefinition子类的情况直接抛出异常
				if (!(beanDef instanceof AbstractBeanDefinition)) {
					throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
							beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
				}
				else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {
					logger.info("Cannot enhance @Configuration bean definition '" + beanName +
							"' since its singleton instance has been created too early. The typical cause " +
							"is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +
							"return type: Consider declaring such methods as 'static'.");
				}
				// 保存下来,准备代理
				configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
			}
		}
		// 如果没有找到 full 配置类,则说明不需要代理增强,则直接返回。
		if (configBeanDefs.isEmpty()) {
			// nothing to enhance -> return immediately
			return;
		}
		// 创建增强对象
		ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
		// 进行配置类增强。这里的增强实际上是通过cglib对配置类进行了代理。
		for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
			AbstractBeanDefinition beanDef = entry.getValue();
			// If a @Configuration class gets proxied, always proxy the target class
			// 设置 :如果配置类被代理,则该 bean也需要一直代理
			beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
			// Set enhanced subclass of the user-specified bean class
			// 获取bean的 Class 类
			Class<?> configClass = beanDef.getBeanClass();
			// 生成代理类
			Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
			if (configClass != enhancedClass) {
				if (logger.isTraceEnabled()) {
					logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
							"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
				}
				 //将BeanClass设置为增强后的类
				beanDef.setBeanClass(enhancedClass);
			}
		}
	}

我们可以看到关键代码在于

Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);

首先我们来看

	// 加载指定的类并为其生成一个CGLIB子类,该子类配备了能够识别作用域和其他bean语义的容器感知回调。
	public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
		// 如果是 EnhancedConfiguration子类,则说明已经被增强(代理),直接返回
		if (EnhancedConfiguration.class.isAssignableFrom(configClass)) {
			... 忽略日志打印
			return configClass;
		}
		// 创建代理类
		Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
		if (logger.isTraceEnabled()) {
			logger.trace(String.format("Successfully enhanced %s; enhanced class name is: %s",
					configClass.getName(), enhancedClass.getName()));
		}
		return enhancedClass;
	}

接下来我们需要看 Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));。我们先来看其中的 newEnhancer(configClass, classLoader) 方法

1.1 newEnhancer(configClass, classLoader)

动态代理参考https://segmentfault.com/a/1190000020633405?utm_source=tag-newest
回调过滤器部分: https://blog.csdn.net/iteye_13303/article/details/82640029


这里创建了一个 Cglib 代理的实例


	private static final ConditionalCallbackFilter CALLBACK_FILTER = new ConditionalCallbackFilter(CALLBACKS);


private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
    // Spring重新打包了CGLIB(使用Spring专用补丁;仅供内部使用)
    // 这样可避免在应用程序级别或第三方库和框架上与CGLIB的依赖性发生任何潜在冲突
    // https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/cglib/package-summary.html
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(configSuperClass);
    // 设置需要实现的接口,也就是说,我们的配置类的cglib代理还实现的 EnhancedConfiguration 接口
    enhancer.setInterfaces(new Class<?>[]{EnhancedConfiguration.class});
    enhancer.setUseFactory(false);
    // 设置命名策略
    enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
    // 设置生成器创建字节码策略
    // BeanFactoryAwareGeneratorStrategy 是 CGLIB的DefaultGeneratorStrategy的自定义扩展,主要为了引入BeanFactory字段
    enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
    // 设置回调过滤器。通过其,可以设置对不同方法执行不同的回调逻辑,或者根本不执行回调。
    enhancer.setCallbackFilter(CALLBACK_FILTER);
    enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
    return enhancer;
}

这里的Enhancer对象是org.springframework.cglib.proxy.Enhancer,那它和cglib是什么关系呢?
大致就是说,Spring重新打包了CGLIB(使用Spring专用补丁,仅供内部使用) ,这样可避免在应用程序级别或第三方库和框架上与CGLIB的依赖性发生任何潜在冲突。

那具体做了哪些增强呢?

  • 实现EnhancedConfiguration接口。这是一个空的标志接口,仅由Spring框架内部使用,并且由所有@ConfigurationCGLIB子类实现,该接口继承了BeanFactoryAware接口。
  • 设置了命名策略
  • 设置生成器创建字节码的策略。BeanFactoryAwareGeneratorStrategy继承了cglib的DefaultGeneratorStrategy,其主要作用是为了让子类引入BeanFactory字段和设置ClassLoader。
  • 设置增强Callback:

1.2 createClass(newEnhancer(configClass, classLoader));

这里是真正创建了一个代理对象了。

	private Class<?> createClass(Enhancer enhancer) {
		Class<?> subclass = enhancer.createClass();
		// Registering callbacks statically (as opposed to thread-local)
		// is critical for usage in an OSGi environment (SPR-5932)...
		// 指定代理回调 为 CALLBACKS
		Enhancer.registerStaticCallbacks(subclass, CALLBACKS);
		return subclass;
	}

Enhancer.registerStaticCallbacks(subclass, CALLBACKS); 调用了 setCallbacksHelper 方法。

	private static void setCallbacksHelper(Class type, Callback[] callbacks, String methodName) {
		// TODO: optimize
		try {
			// 反射调用方法,并将回调函数传入。
			Method setter = getCallbacksSetter(type, methodName);
			setter.invoke(null, new Object[]{callbacks});
		}
		catch (NoSuchMethodException e) {
			throw new IllegalArgumentException(type + " is not an enhanced class");
		}
		catch (IllegalAccessException e) {
			throw new CodeGenerationException(e);
		}
		catch (InvocationTargetException e) {
			throw new CodeGenerationException(e);
		}
	}

这里我们可以知道,这里创建了一个 @Bean 生成的 对象 的增强代理,同时通过 ConditionalCallbackFilter 的回调过滤器和指定的回调函数CALLBACKS,完成了增强的过程。

但是对于我们,我们还需要看一下回调函数 CALLBACKS中完成了什么操作。

2. 回调函数

CALLBACKS 定义如下。

	private static final Callback[] CALLBACKS = new Callback[] {
			// 拦截@Bean方法的调用,以确保正确处理@Bean语义
			new BeanMethodInterceptor(),
			// BeanFactoryAware#setBeanFactory的调用,用于获取BeanFactory对象
			new BeanFactoryAwareMethodInterceptor(),
			NoOp.INSTANCE
	};

下面我们来看看两个拦截器的拦截方法

2.1 BeanMethodInterceptor#intercept

	public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
				MethodProxy cglibMethodProxy) throws Throwable {
		// 获取beanFactory
		ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
		// 根据从配置类中的方法获取beanName
		String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);

		// Determine whether this bean is a scoped-proxy
		// 确定此bean是否为作用域代理。即判断是否包含 @Scope注解,并且其属性 proxyMode 不为  ScopedProxyMode.NO。
		if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
			String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
			if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
				beanName = scopedBeanName;
			}
		}
		// 官方注释 : 要处理Bean间方法引用,我们必须显式检查容器中是否已缓存实例。首先,检查所请求的bean是否为FactoryBean。
		//如果是这样,则创建一个子类代理,以拦截对getObject()的调用并返回所有缓存的Bean实例。
		//这样可以确保从@Bean方法中调用FactoryBean的语义与在XML中引用FactoryBean的语义相同
		
		// 判断当前BeanFactory 中是否 存在当前bean的FactoryBean实例 && 包含bean实例 。说白了就是检查容器中是否已经存在该bean 的缓存实例,如果存在需要进行代理
		if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
				factoryContainsBean(beanFactory, beanName)) {
			// 获取bean对应 FactoryBean 实例。对FactoryBean进行代理
			Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
			if (factoryBean instanceof ScopedProxyFactoryBean) {
				// Scoped proxy factory beans are a special case and should not be further proxied
				//范围限定的代理工厂bean是一种特殊情况,不应进一步进行代理
			}
			else {
				// It is a candidate FactoryBean - go ahead with enhancement
				// 它是候选FactoryBean-继续进行增强
				return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
			}
		}
		// 这里是我们一般的逻辑,
		// isCurrentlyInvokedFactoryMethod 判断的是,是否是Spring容器自己调用@Bean 方法而并非我们自己编写代码调用。如果是Spring直接调用真正的@Bean方法,这时候多次调用返回的并非同一实例
		if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
			// 工厂正在调用bean方法以便实例化和注册bean(即通过getBean()调用)->调用该方法的超级实现以实际创建bean实例。
			// 这里调用的就是未被增强的 @Bean 方法
			return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
		}
		// 返回cglib 代理后的实例。如果没有创建则创建
		return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
	}

2.1.1 enhanceFactoryBean

该方法是对 FactoryBean 进行动态代理。

这里注意:对于 FactoryBean的类型的处理,首先判断了类或者getObject 方法是否是终态(被final 修饰),因为cglib 代理是通过继承代理类来实现的代理,所以这里如果是终态则无法代理。如果方法返回类型是接口,则说明是多态,可以使用实现接口的方式来进行代理。也就说在这个方法里面根据是否@Bean 方法是否是接口方法来选择使用 Cglib代理和 Jdk动态代理两种方式。

关于 FactoryBean 的介绍,请移步:Spring 源码分析衍生篇一:FactoryBean介绍

	private Object enhanceFactoryBean(final Object factoryBean, Class<?> exposedType,
			final ConfigurableBeanFactory beanFactory, final String beanName) {

		try {
			Class<?> clazz = factoryBean.getClass();
			boolean finalClass = Modifier.isFinal(clazz.getModifiers());
			boolean finalMethod = Modifier.isFinal(clazz.getMethod("getObject").getModifiers());
			// 判断,如果类是 final修饰 ||  getObject 方法被final 修饰
			// 因为 cglib 代理是通过创建一个类继承代理类实现,所以这里如果被final修饰就要另谋处理
			if (finalClass || finalMethod) {
				// 如果方法的返回类型是接口,则说明使用了多态
				// 则可以创建一个接口的实现类来代理FactoryBean
				if (exposedType.isInterface()) {
					
					return createInterfaceProxyForFactoryBean(factoryBean, exposedType, beanFactory, beanName);
				}
				else {
					// 如果不是,则没办法进行代理,直接返回FactoryBean。
					return factoryBean;
				}
			}
		}
		catch (NoSuchMethodException ex) {
			// No getObject() method -> shouldn't happen, but as long as nobody is trying to call it...
		}
		// 直接进行代理
		return createCglibProxyForFactoryBean(factoryBean, beanFactory, beanName);
	}

	...
	// 创建 JDK动态代理
	private Object createInterfaceProxyForFactoryBean(final Object factoryBean, Class<?> interfaceType,
			final ConfigurableBeanFactory beanFactory, final String beanName) {
		// 可以看到,实际上代理的是 FactoryBean 的 getObject 方法
		return Proxy.newProxyInstance(
				factoryBean.getClass().getClassLoader(), new Class<?>[] {interfaceType},
				(proxy, method, args) -> {
					if (method.getName().equals("getObject") && args == null) {
						return beanFactory.getBean(beanName);
					}
					return ReflectionUtils.invokeMethod(method, factoryBean, args);
				});
	}

2.2 BeanFactoryAwareMethodInterceptor#intercept

BeanFactoryAwareMethodInterceptor#intercept 代码很简单,如下

		@Override
		@Nullable
		public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
			// 获取 obj中的 “$$beanFactory” 属性(BEAN_FACTORY_FIELD 即为 "$$beanFactory")
			Field field = ReflectionUtils.findField(obj.getClass(), BEAN_FACTORY_FIELD);
			Assert.state(field != null, "Unable to find generated BeanFactory field");
			// 将参数 arg[0] 设置给 Obj 的 "$$beanFactory" 属性
			field.set(obj, args[0]);

			// Does the actual (non-CGLIB) superclass implement BeanFactoryAware?
			// If so, call its setBeanFactory() method. If not, just exit.
			if (BeanFactoryAware.class.isAssignableFrom(ClassUtils.getUserClass(obj.getClass().getSuperclass()))) {
				return proxy.invokeSuper(obj, args);
			}
			return null;
		}

四、总结

  1. @Bean@Component 中 是多例的原因?
    因为 ConfigurationClassPostProcessor 方法中 只对 full 类型的配置类(即被 @Configuration 注解修饰)进行了代理,因此被 @Component 修饰的类并不会被代理,自然也就不会保持单例。
  2. ConfigurationClassPostProcessor#postProcessBeanFactory 方法完成了对 full 类型的配置类(即被 @Configuration 注解修饰)进行了代理 保证了语义的正确性。

以上:内容部分参考
《Spring源码深度解析》
https://segmentfault.com/a/1190000020633405?utm_source=tag-newest
如有侵扰,联系删除。 内容仅用于自我记录学习使用。如有错误,欢迎指正

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猫吻鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值