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

一、前言

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


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


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

1. ConfigurationClassPostProcessor

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

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

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

void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

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


上篇 分析了 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() 方法返回的结果则不相同。

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

三、 代码分析

postProcessBeanFactory 方法代码如下:

	@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();
			}
			// 如果是配置类或者 @Bean注解派生的配置类
			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;
				if (!abd.hasBeanClass()) {
    
    
					try {
    
    
						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)) {
    
    
				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
			beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
			// Set enhanced subclass of the user-specified 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);

首先我们来看

	public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
    
    
		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;
	}

不言而喻。
我们先来看 newEnhancer(configClass, classLoader) 方法

1.1 newEnhancer(configClass, classLoader)

这里参考该文

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)...
		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);
		}
	}

这里并没有什么多余的操作,可以看到主要的操作还是在回调函数中,下面我们来看看回调函数

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的FactoryBean实例 && 包含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
				}
				else {
    
    
					// It is a candidate FactoryBean - go ahead with enhancement
					// 进行代理
					return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
				}
			}
			// 这里是我们一般的逻辑,
			// isCurrentlyInvokedFactoryMethod 判断的是,是否是Spring容器自己调用@Bean 方法。如果是则直接调用真正的@Bean方法,这时候多次调用返回的并非同一实例
			if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
    
    
				
				return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
			}
			// 返回cglib 代理后的实例。如果没有创建则创建
			return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
		}

这里注意:对于 FactoryBean的类型的处理,首先判断了类或者getObject 方法是否是终态(被final 修饰),因为cglib 代理是通过继承代理类来实现的代理,所以这里如果是终态则无法代理。如果方法返回类型是接口,则说明是多态,可以使用实现接口的方式来进行代理。

关于 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);
		}

...

	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
如有侵扰,联系删除。 内容仅用于自我记录学习使用。如有错误,欢迎指正

猜你喜欢

转载自blog.csdn.net/qq_36882793/article/details/106652607