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;
}
四、总结
@Bean在@Component中 是多例的原因?
因为ConfigurationClassPostProcessor方法中 只对full类型的配置类(即被@Configuration注解修饰)进行了代理,因此被@Component修饰的类并不会被代理,自然也就不会保持单例。ConfigurationClassPostProcessor#postProcessBeanFactory方法完成了对full类型的配置类(即被@Configuration注解修饰)进行了代理 保证了语义的正确性。
以上:内容部分参考
《Spring源码深度解析》
https://segmentfault.com/a/1190000020633405?utm_source=tag-newest
如有侵扰,联系删除。 内容仅用于自我记录学习使用。如有错误,欢迎指正