前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring5 源码学习 (7) ConfigurationClassPostProcessor (下)

Spring5 源码学习 (7) ConfigurationClassPostProcessor (下)

作者头像
Coder小黑
发布2019-12-18 14:50:43
8260
发布2019-12-18 14:50:43
举报
文章被收录于专栏:Coder小黑Coder小黑

ConfigurationClassPostProcessor继承了BeanDefinitionRegistryPostProcessor接口,它实现了postProcessBeanDefinitionRegistry和其父类的BeanFactoryPostProcessor#postProcessBeanFactory方法。

关于postProcessBeanDefinitionRegistry方法的解析可以参看:Spring5 源码学习 (5) ConfigurationClassPostProcessor (上)

现在我们来看一下ConfigurationClassPostProcessor#postProcessBeanFactory方法的源码。


ConfigurationClassPostProcessor#postProcessBeanFactory

调用时机

ConfigurationClassPostProcessor#postProcessBeanFactory方法也在refresh();方法中执行invokeBeanFactoryPostProcessors(beanFactory);方法时被调用的。

源码解析

代码语言:javascript
复制
//ConfigurationClassPostProcessor#postProcessBeanFactory源码
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);
	// 在 this.postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) 方法中
	// 调用this.registriesPostProcessed.add(registryId);
	// if条件不成立
	if (!this.registriesPostProcessed.contains(factoryId)) {
		// BeanDefinitionRegistryPostProcessor hook apparently not supported...
		// Simply call processConfigurationClasses lazily at this point then.
		processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
	}

	// 对配置类进行增强
	enhanceConfigurationClasses(beanFactory);
	// 创建 ImportAwareBeanPostProcessor ,来支持 ImportAware ,调用ImportAware.setImportMetadata方法
	beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}

主要做了两件事:

  1. 对配置类进行增强
  2. 创建ImportAwareBeanPostProcessor 来支持ImportAware接口。

主要来看一下对配置类进行增强方法enhanceConfigurationClasses(beanFactory);的源码。

enhanceConfigurationClasses(beanFactory)增强 Full Configuration

Spring会对Full Configuration 进行代理,拦截@Bean方法,以确保正确处理@Bean语义。这个增强的代理类就是在enhanceConfigurationClasses(beanFactory)方法中产生的,源码如下:

代码语言:javascript
复制
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
	Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
	//获取所有的BeanDefinitionName,之前已经完成了bean的扫描,这里会获取到所有的beanName
	for (String beanName : beanFactory.getBeanDefinitionNames()) {
		BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
		// 校验是否为FullConfigurationClass,也就是是否被标记了 @Configuration
		if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {
			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'.");
			}
			//如果是FullConfigurationClass,则放到变量configBeanDefs中
			configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
		}
	}
	if (configBeanDefs.isEmpty()) {
		// nothing to enhance -> return immediately
		return;
	}

	ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
	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);
		try {
			// Set enhanced subclass of the user-specified bean class
			Class<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader);
			if (configClass != null) {
				// 对 FullConfigurationClass 进行增强
				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);
				}
			}
		} catch (Throwable ex) {
			throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
		}
	}
}

获取所有为FullConfigurationClass的BeanDefinition(即标注@Configuration的配置类),然后依次调用enhancer.enhance(configClass, this.beanClassLoader);方法,对配置类进行增强,将方法返回Class<?> enhancedClass的设置到BeanDefinition中(eanDef.setBeanClass(enhancedClass);),之后Spring创建该BeanDefinition时就会使用这个增强类来创建。

下面是enhancer.enhance(configClass, this.beanClassLoader);方法源码:

代码语言:javascript
复制
public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
	if (EnhancedConfiguration.class.isAssignableFrom(configClass)) {
		if (logger.isDebugEnabled()) {
			logger.debug(String.format("Ignoring request to enhance %s as it has " +
							"already been enhanced. This usually indicates that more than one " +
							"ConfigurationClassPostProcessor has been registered (e.g. via " +
							"<context:annotation-config>). This is harmless, but you may " +
							"want check your configuration and remove one CCPP if possible",
					configClass.getName()));
		}
		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;
}
创建Full Configuration增强类

具体来看一下newEnhancer(configClass, classLoader方法,这个方法负责创建Full Configuration增强类。

代码语言:javascript
复制
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's repackaging of CGLIB 3.2 (with Spring-specific patches; for internal use only).This repackaging technique avoids any potential conflicts with dependencies on CGLIB at the application level or from third-party libraries and frameworks. 引用自:https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/cglib/package-summary.html

大致就是说,Spring重新打包了CGLIB(使用Spring专用补丁,仅供内部使用) ,这样可避免在应用程序级别或第三方库和框架上与CGLIB的依赖性发生任何潜在冲突。

那具体做了哪些增强呢?

  1. 实现EnhancedConfiguration接口。这是一个空的标志接口,仅由Spring框架内部使用,并且由所有@ConfigurationCGLIB子类实现,该接口继承了BeanFactoryAware接口。
  2. 设置了命名策略
  3. 设置生成器创建字节码的策略。BeanFactoryAwareGeneratorStrategy继承了cglib的DefaultGeneratorStrategy,其主要作用是为了让子类引入BeanFactory字段和设置ClassLoader
  4. 设置增强Callback
代码语言:javascript
复制
private static final Callback[] CALLBACKS = new Callback[]{
		// 拦截 @Bean 方法的调用,以确保正确处理@Bean语义
		new BeanMethodInterceptor(),
		// 拦截 BeanFactoryAware#setBeanFactory 的调用
		new BeanFactoryAwareMethodInterceptor(),
		NoOp.INSTANCE
};
  • BeanMethodInterceptor:负责拦截@Bean方法的调用,以确保正确处理@Bean语义。
  • BeanFactoryAwareMethodInterceptor:负责拦截 BeanFactoryAware#setBeanFactory方法的调用,因为增强的配置类实现了EnhancedConfiguration接口(也就是实现了BeanFactoryAwar接口)。
设置增强Callback

下面,我们就以AppConfig为例,来学习增强Callback相关源码。

代码语言:javascript
复制
@Configuration
@ComponentScan
public class AppConfig {

	@Bean
	public String name() throws Exception {
		getUserBean().getObject();
		return "程序员小黑";
	}


	@Bean
	public FactoryBean getUserBean() {
		return new FactoryBean<UserBean>() {
			@Override
			public UserBean getObject() throws Exception {
				System.out.println("1111");
				return new UserBean("shen", 17);
			}

			@Override
			public Class<?> getObjectType() {
				return UserBean.class;
			}
		};
	}
}

BeanMethodInterceptor

主要作用是:拦截@Bean方法的调用,以确保正确处理@Bean语义。当调用@Bean方法时,就会被以下代码所拦截:

代码语言:javascript
复制
//BeanMethodInterceptor#intercept源码
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
						MethodProxy cglibMethodProxy) throws Throwable {

	// enhancedConfigInstance 已经是配置类的增强对象了,在增强对象中,有beanFactory字段的
	// 获取增强对象中的beanFactory
	ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
	// 获取beanName
	String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);

	// Determine whether this bean is a scoped-proxy
	if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
		String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
		if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
			beanName = scopedBeanName;
		}
	}

	// To handle the case of an inter-bean method reference, we must explicitly check the
	// container for already cached instances.

	// First, check to see if the requested bean is a FactoryBean. If so, create a subclass
	// proxy that intercepts calls to getObject() and returns any cached bean instance.
	// This ensures that the semantics of calling a FactoryBean from within @Bean methods
	// is the same as that of referring to a FactoryBean within XML. See SPR-6602.

	// 检查容器中是否存在对应的 FactoryBean 如果存在,则创建一个增强类
	// 通过创建增强类来代理拦截 getObject()的调用 , 以确保了FactoryBean的语义
	if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
			factoryContainsBean(beanFactory, beanName)) {
		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
			// 创建增强类,来代理 getObject()的调用
			// 有两种可选代理方式,cglib 和 jdk
			// 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);
			//					});
			return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
		}
	}

	// 判断当时执行的方法是否为@Bean方法本身
	// 举个例子 : 如果是直接调用@Bean方法,也就是Spring来调用我们的@Bean方法,则返回true
	// 如果是在别的方法内部,我们自己的程序调用 @Bean方法,则返回false
	if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
		// The factory is calling the bean method in order to instantiate and register the bean
		// (i.e. via a getBean() call) -> invoke the super implementation of the method to actually
		// create the bean instance.
		if (logger.isInfoEnabled() &&
				BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) {
			logger.info(String.format("@Bean method %s.%s is non-static and returns an object " +
							"assignable to Spring's BeanFactoryPostProcessor interface. This will " +
							"result in a failure to process annotations such as @Autowired, " +
							"@Resource and @PostConstruct within the method's declaring " +
							"@Configuration class. Add the 'static' modifier to this method to avoid " +
							"these container lifecycle issues; see @Bean javadoc for complete details.",
					beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName()));
		}
		// 如果返回true,也就是Spring在调用这个方法,那么就去真正执行该方法
		return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
	}

	//否则,则尝试从容器中获取该 Bean 对象
	// 怎么获取呢? 通过调用 beanFactory.getBean 方法
	// 而这个getBean 方法,如果对象已经创建则直接返回,如果还没有创建,则创建,然后放入容器中,然后返回
	return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}
  1. enhancedConfigInstance是配置类的增强对象。从增强对象中获取beanFactorybeanName。举个例子:当Spring调用name()方法时,beanName就是name
  2. 检查容器中是否存在对应的FactoryBean,如果存在,则创建一个增强类,来代理getObject()的调用。在本示例中,如果读者将name()方法注释删掉之后程序并不会执行到这一步。因为Spring调用getUserBean()方法时,容器中并没有存在对应的FactoryBean。因为只有第二次调用getUserBean()方法容器中才会存在对应的FactoryBean
  3. 判断当时执行的方法是否为@Bean方法本身,如果是,则直接调用该方法,不做增强拦截;否则,则尝试从容器中获取该Bean对象。
BeanFactoryAwareMethodInterceptor

BeanFactoryAwareMethodInterceptor方法就比较简单,其作用为拦截 BeanFactoryAware#setBeanFactory的调用,用于获取BeanFactory对象。

代码语言:javascript
复制
// BeanFactoryAwareMethodInterceptor#intercept 源码
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
	Field field = ReflectionUtils.findField(obj.getClass(), BEAN_FACTORY_FIELD);
	Assert.state(field != null, "Unable to find generated BeanFactory field");
	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;
}
输出增强类class文件

最后,再补充说明一点,我们可以通过如下配置来获取Spring为我们生成的CGLIB代理增强类的class文件:

代码语言:javascript
复制
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "spring-study/docs/classes");

源码注释GITHUB地址:https://github.com/shenjianeng/spring-code-study

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-09-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Coder小黑 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ConfigurationClassPostProcessor#postProcessBeanFactory
    • 调用时机
      • 源码解析
        • enhanceConfigurationClasses(beanFactory)增强 Full Configuration
        • 创建Full Configuration增强类
        • 设置增强Callback
        • 输出增强类class文件
    相关产品与服务
    容器服务
    腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档