基于 Spring Framework v5.2.6.RELEASE
接上篇:Spring 源码阅读 38:postProcessBeanFactory 对 @Configuration 配置的解析和处理(1)
概述
上篇介绍了后处理器的postProcessBeanFactory
方法通过enhanceConfigurationClasses
方法对配置类进行增强的前半部分,也就是从容器中获取并筛选出需要进行增强处理的配置类。本文将深入分析增强处理的原理。
处理过程
本文的分析内容对应到代码就是enhanceConfigurationClasses
中的一下这部分:
ConfigurationClassEnhancerenhancer=newConfigurationClassEnhancer(); for (Map.Entry<String, AbstractBeanDefinition>entry : configBeanDefs.entrySet()) { AbstractBeanDefinitionbeanDef=entry.getValue(); // If a @Configuration class gets proxied, always proxy the target classbeanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE); // Set enhanced subclass of the user-specified bean classClass<?>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())); } beanDef.setBeanClass(enhancedClass); } }
其中用到的configBeanDefs
集合,就是筛选出的需要进行增强处理的配置类的集合。
在进入for
循环之前,首先创建了一个 ConfigurationClassEnhancer 类型的对象enhancer
,从名称可以看出,它用来对配置类进行增强处理。
在for
循环语句块中,首先会获取到当前遍历到的 BeanDefinition 对象,类型是 AbstractBeanDefinition。然后给其设置属性org.springframework.aop.framework.autoproxy.AutoProxyUtils.preserveTargetClass
的值为true
。
然后获取到当前 BeanDefinition 的类型信息configClass
,通过enhancer
的enhance
方法,得到增强后的类型enhancedClass
。
最后,判断configClass
和enhancedClass
不是同一个对象,将enhancedClass
设置为BeanDefinition 新的类型信息。
可以看出,整个过程比较简单,而enhance
方法是这里的核心方法,下面我们分析enhance
方法的原理。
enhance
方法
进入方法内部。
// org.springframework.context.annotation.ConfigurationClassEnhancer#enhancepublicClass<?>enhance(Class<?>configClass, ClassLoaderclassLoader) { 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())); } returnconfigClass; } 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())); } returnenhancedClass; }
如果方法传入的configClass
是 EnhancedConfiguration 类型,则说明已经被增强过,直接返回。如果不是的话,则通过newEnhancer
方法,得到一个 Enhancer 对象,然后通过createClass
方法,进行处理,并将结果返回。
先查看newEnhancer
方法的处理逻辑。
// org.springframework.context.annotation.ConfigurationClassEnhancer#newEnhancer/*** Creates a new CGLIB {@link Enhancer} instance.*/privateEnhancernewEnhancer(Class<?>configSuperClass, ClassLoaderclassLoader) { Enhancerenhancer=newEnhancer(); enhancer.setSuperclass(configSuperClass); enhancer.setInterfaces(newClass<?>[] {EnhancedConfiguration.class}); enhancer.setUseFactory(false); enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); enhancer.setStrategy(newBeanFactoryAwareGeneratorStrategy(classLoader)); enhancer.setCallbackFilter(CALLBACK_FILTER); enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes()); returnenhancer; }
这里可以看出,Spring 对配置类的增强使用的是 CGLIB 的方式。这里给新创建的enhancer
设置了一些信息,这些信息先留个印象,之后遇到了我们再回过来介绍。
创建好的enhancer
会作为参数传入createClass
方法中,我们进入createClass
方法查看代码。
// org.springframework.context.annotation.ConfigurationClassEnhancer#createClassprivateClass<?>createClass(Enhancerenhancer) { 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); returnsubclass; }
这里通过刚刚创建的enhancer
创建了增强的子类,在返回子类之前,通过 Enhancer 的registerStaticCallbacks
方法为这个子类注册回调接口。常量CALLBACKS
包含注册的回调接口。
// org.springframework.context.annotation.ConfigurationClassEnhancer#CALLBACKSprivatestaticfinalCallback[] CALLBACKS=newCallback[] { newBeanMethodInterceptor(), newBeanFactoryAwareMethodInterceptor(), NoOp.INSTANCE};
下面我们逐个来看 BeanMethodInterceptor 和 BeanFactoryAwareMethodInterceptor 方法拦截器。
BeanMethodInterceptor
先看 BeanMethodInterceptor 拦截器,方法拦截器会根据isMatch方法筛选要拦截的方法。
// org.springframework.context.annotation.ConfigurationClassEnhancer.BeanMethodInterceptor#isMatchpublicbooleanisMatch(MethodcandidateMethod) { return (candidateMethod.getDeclaringClass() !=Object.class&&!BeanFactoryAwareMethodInterceptor.isSetBeanFactory(candidateMethod) &&BeanAnnotationHelper.isBeanAnnotated(candidateMethod)); }
由上面的代码可知,BeanMethodInterceptor 拦截的是有返回值、不是 BeanFactoryAware 接口的setBeanFactory
方法,且被@Bean
注解标记的方法。
拦截器的主要逻辑在它的intercept
方法中。
代码量比较大,下面开始分析。
准备工作
ConfigurableBeanFactorybeanFactory=getBeanFactory(enhancedConfigInstance);
首先,通过getBeanFactory
方法,从增强类实例中获取 BeanFactory 容器,进入方法查看源码。
privateConfigurableBeanFactorygetBeanFactory(ObjectenhancedConfigInstance) { Fieldfield=ReflectionUtils.findField(enhancedConfigInstance.getClass(), BEAN_FACTORY_FIELD); Assert.state(field!=null, "Unable to find generated bean factory field"); ObjectbeanFactory=ReflectionUtils.getField(field, enhancedConfigInstance); Assert.state(beanFactory!=null, "BeanFactory has not been injected into @Configuration class"); Assert.state(beanFactoryinstanceofConfigurableBeanFactory, "Injected BeanFactory is not a ConfigurableBeanFactory"); return (ConfigurableBeanFactory) beanFactory; }
从源码中可以看出,容器对象就是增强配置类的$$beanFactory
字段,这个字段是在创建增强类型的时候添加的,赋值的方式可以参考后文介绍的 BeanFactoryAwareMethodInterceptor 拦截器的逻辑。
再回到intercept
方法中。
StringbeanName=BeanAnnotationHelper.determineBeanNameFor(beanMethod);
接下来,获取 BeanAnnotationHelper 的determineBeanNameFor
方法,获取了被@
Bean
标记的方法对应的 Bean 名称。获取的代码如下:
// org.springframework.context.annotation.BeanAnnotationHelper#determineBeanNameForpublicstaticStringdetermineBeanNameFor(MethodbeanMethod) { StringbeanName=beanNameCache.get(beanMethod); if (beanName==null) { // By default, the bean name is the name of the @Bean-annotated methodbeanName=beanMethod.getName(); // Check to see if the user has explicitly set a custom bean name...AnnotationAttributesbean=AnnotatedElementUtils.findMergedAnnotationAttributes(beanMethod, Bean.class, false, false); if (bean!=null) { String[] names=bean.getStringArray("name"); if (names.length>0) { beanName=names[0]; } } beanNameCache.put(beanMethod, beanName); } returnbeanName; }
默认情况下,会把方法名作为 Bean 的名称,如果@
Bean
注解的name
属性设置了值,则将其作为 Bean 的名称。
再看intercept
方法中后续的代码。
// Determine whether this bean is a scoped-proxyif (BeanAnnotationHelper.isScopedProxy(beanMethod)) { StringscopedBeanName=ScopedProxyCreator.getTargetBeanName(beanName); if (beanFactory.isCurrentlyInCreation(scopedBeanName)) { beanName=scopedBeanName; } }
如果方法通过@
Scope
指定了作用域代理,则对它的 Bean 名称进行处理。
接着往下看。
增强 FactoryBean
if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX+beanName) &&factoryContainsBean(beanFactory, beanName)) { ObjectfactoryBean=beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX+beanName); if (factoryBeaninstanceofScopedProxyFactoryBean) { // Scoped proxy factory beans are a special case and should not be further proxied } else { // It is a candidate FactoryBean - go ahead with enhancementreturnenhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName); } }
在if
判断条件中,会根据 Bean 名称判断容器中是否有对应的 Bean 以及其对应的 FactoryBean 工厂 Bean,如果都有的话,则说明当前的 Bean 是一个实现了 FactoryBean 的类型。对于符合条件的 Bean,会从容器中获取其对应的 FactoryBean,也就是工厂实例,对其进行增强。
在增强之前,还会判断这个工厂实例是不是 ScopedProxyFactoryBean 类型,如果是的话,则跳过增强的逻辑。最终,增强的工作是通过enhanceFactoryBean
方法完成的。
我们进入enhanceFactoryBean
方法查看源码。
// org.springframework.context.annotation.ConfigurationClassEnhancer.BeanMethodInterceptor#enhanceFactoryBeanprivateObjectenhanceFactoryBean(finalObjectfactoryBean, Class<?>exposedType, finalConfigurableBeanFactorybeanFactory, finalStringbeanName) { try { Class<?>clazz=factoryBean.getClass(); booleanfinalClass=Modifier.isFinal(clazz.getModifiers()); booleanfinalMethod=Modifier.isFinal(clazz.getMethod("getObject").getModifiers()); if (finalClass||finalMethod) { if (exposedType.isInterface()) { if (logger.isTraceEnabled()) { logger.trace("Creating interface proxy for FactoryBean '"+beanName+"' of type ["+clazz.getName() +"] for use within another @Bean method because its "+ (finalClass?"implementation class" : "getObject() method") +" is final: Otherwise a getObject() call would not be routed to the factory."); } returncreateInterfaceProxyForFactoryBean(factoryBean, exposedType, beanFactory, beanName); } else { if (logger.isDebugEnabled()) { logger.debug("Unable to proxy FactoryBean '"+beanName+"' of type ["+clazz.getName() +"] for use within another @Bean method because its "+ (finalClass?"implementation class" : "getObject() method") +" is final: A getObject() call will NOT be routed to the factory. "+"Consider declaring the return type as a FactoryBean interface."); } returnfactoryBean; } } } catch (NoSuchMethodExceptionex) { // No getObject() method -> shouldn't happen, but as long as nobody is trying to call it... } returncreateCglibProxyForFactoryBean(factoryBean, beanFactory, beanName); }
这个方法的逻辑比较清晰。首先会判断类定义和getObject
方法的定义,如果二者至少有一个是final
修饰的,那么就会进入第一个if
语句块。
进入后,会判断@
Bean
方法的返回值,是不是一个接口类型。如果是的话,就通过createInterfaceProxyForFactoryBean
方法创建动态代理并返回,如果不是的话,则直接返回factoryBean
。
这里我们进入createInterfaceProxyForFactoryBean
方法查看一下细节。
// org.springframework.context.annotation.ConfigurationClassEnhancer.BeanMethodInterceptor#createInterfaceProxyForFactoryBeanprivateObjectcreateInterfaceProxyForFactoryBean(finalObjectfactoryBean, Class<?>interfaceType, finalConfigurableBeanFactorybeanFactory, finalStringbeanName) { returnProxy.newProxyInstance( factoryBean.getClass().getClassLoader(), newClass<?>[] {interfaceType}, (proxy, method, args) -> { if (method.getName().equals("getObject") &&args==null) { returnbeanFactory.getBean(beanName); } returnReflectionUtils.invokeMethod(method, factoryBean, args); }); }
可以看到,这里创建代理使用的是JDK动态代理的方式,如果当前调用的方法是getObject
方法且参数为空,那么则返回根据 Bean 名称从容器中获取的 Bean 实例。
回到enhanceFactoryBean
方法中,如果最开始的if
判断条件不成立,则通过createCglibProxyForFactoryBean
方法创建动态代理,从名称可以看出,这里使用的是CGLIB的方式。
// org.springframework.context.annotation.ConfigurationClassEnhancer.BeanMethodInterceptor#createCglibProxyForFactoryBeanprivateObjectcreateCglibProxyForFactoryBean(finalObjectfactoryBean, finalConfigurableBeanFactorybeanFactory, finalStringbeanName) { Enhancerenhancer=newEnhancer(); enhancer.setSuperclass(factoryBean.getClass()); enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); enhancer.setCallbackType(MethodInterceptor.class); // Ideally create enhanced FactoryBean proxy without constructor side effects,// analogous to AOP proxy creation in ObjenesisCglibAopProxy...Class<?>fbClass=enhancer.createClass(); ObjectfbProxy=null; if (objenesis.isWorthTrying()) { try { fbProxy=objenesis.newInstance(fbClass, enhancer.getUseCache()); } catch (ObjenesisExceptionex) { logger.debug("Unable to instantiate enhanced FactoryBean using Objenesis, "+"falling back to regular construction", ex); } } if (fbProxy==null) { try { fbProxy=ReflectionUtils.accessibleConstructor(fbClass).newInstance(); } catch (Throwableex) { thrownewIllegalStateException("Unable to instantiate enhanced FactoryBean using Objenesis, "+"and regular FactoryBean instantiation via default constructor fails as well", ex); } } ((Factory) fbProxy).setCallback(0, (MethodInterceptor) (obj, method, args, proxy) -> { if (method.getName().equals("getObject") &&args.length==0) { returnbeanFactory.getBean(beanName); } returnproxy.invoke(factoryBean, args); }); returnfbProxy; }
这里的代码量虽然相对较多,但是代理的逻辑跟前面通过JDK动态代理实现的逻辑是一样的。
增强普通类型的 Bean
接着看intercept
方法中后续的代码。
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())); } returncglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs); }
判断条件中调用了isCurrentlyInvokedFactoryMethod
方法,我们查看其判断逻辑。
// org.springframework.context.annotation.ConfigurationClassEnhancer.BeanMethodInterceptor#isCurrentlyInvokedFactoryMethodprivatebooleanisCurrentlyInvokedFactoryMethod(Methodmethod) { MethodcurrentlyInvoked=SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod(); return (currentlyInvoked!=null&&method.getName().equals(currentlyInvoked.getName()) &&Arrays.equals(method.getParameterTypes(), currentlyInvoked.getParameterTypes())); }
这里判断了给定的方法,是否是配置类中的工厂方法,且工厂方法就是当前的方法。如果是,则调用父类中的方法创建实例。
最后,如果当前调用的方法对应的 Bean 名称不能在 Spring 容器中找到 FactoryBean,而且也没有定义工厂方法,则会执行resolveBeanReference
方法。
returnresolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
进入resolveBeanReference
方法。
这个方法虽然很长,但是仔细看就会发现逻辑很简单,就是使用 Bean 的名称,从容器中获取实例。
至此,BeanMethodInterceptor 的逻辑就介绍完了,下面看 BeanFactoryAwareMethodInterceptor。
BeanFactoryAwareMethodInterceptor
先看isMatch
方法。
// org.springframework.context.annotation.ConfigurationClassEnhancer.BeanFactoryAwareMethodInterceptor#isMatchpublicbooleanisMatch(MethodcandidateMethod) { returnisSetBeanFactory(candidateMethod); } publicstaticbooleanisSetBeanFactory(MethodcandidateMethod) { return (candidateMethod.getName().equals("setBeanFactory") &&candidateMethod.getParameterCount() ==1&&BeanFactory.class==candidateMethod.getParameterTypes()[0] &&BeanFactoryAware.class.isAssignableFrom(candidateMethod.getDeclaringClass())); }
判断条件比较多,总结起来就是,判断当前的方法是不是 BeanFactoryAware 感知接口的setBeanFactory
实现方法。
再看intercept
方法。
// org.springframework.context.annotation.ConfigurationClassEnhancer.BeanFactoryAwareMethodInterceptor#interceptpublicObjectintercept(Objectobj, Methodmethod, Object[] args, MethodProxyproxy) throwsThrowable { Fieldfield=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()))) { returnproxy.invokeSuper(obj, args); } returnnull; }
首先会判断增强类中是否存在名称为$$beanFactory
的字段(常量BEAN_FACTORY_FIELD
的值),如果存在的话,就会为该字段赋值,这样,增强后的配置类就能回去到 BeanFactory 容器了。
这个$$beanFactory
是怎么来的呢?
在之前介绍过的newEnhancer
方法创建增强类的时候,有这样一行代码:
enhancer.setStrategy(newBeanFactoryAwareGeneratorStrategy(classLoader));
这里给增强类设置了一个策略,在 BeanFactoryAwareGeneratorStrategy 中对配置类进行增强时,添加了$$beanFactory
字段。
// org.springframework.context.annotation.ConfigurationClassEnhancer.BeanFactoryAwareGeneratorStrategy#transformprotectedClassGeneratortransform(ClassGeneratorcg) throwsException { ClassEmitterTransformertransformer=newClassEmitterTransformer() { publicvoidend_class() { declare_field(Constants.ACC_PUBLIC, BEAN_FACTORY_FIELD, Type.getType(BeanFactory.class), null); super.end_class(); } }; returnnewTransformingClassGenerator(cg, transformer); }
最后的部分
到这里,enhance
方法的全部逻辑就都介绍完了,我们再回到最初调用enhance
方法的部分。
for (Map.Entry<String, AbstractBeanDefinition>entry : configBeanDefs.entrySet()) { AbstractBeanDefinitionbeanDef=entry.getValue(); // If a @Configuration class gets proxied, always proxy the target classbeanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE); // Set enhanced subclass of the user-specified bean classClass<?>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())); } beanDef.setBeanClass(enhancedClass); } }
在for循环中,每获得一个配置类的增强类之后,就会将其对应的 BeanDefinition 类型信息设置为增强类型。
总结
本文分析了postProcessBeanFactory
处理方法调用的enhanceConfigurationClasses
方法对配置类进行增强处理的后半部分流程,也就是对配置类进行增强的逻辑。