持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第10天,点击查看活动详情
基于 Spring Framework v5.2.6.RELEASE
接上篇:Spring 源码阅读 38:postProcessBeanFactory 对 @Configuration 配置的解析和处理(1)
概述
上篇介绍了后处理器的postProcessBeanFactory方法通过enhanceConfigurationClasses方法对配置类进行增强的前半部分,也就是从容器中获取并筛选出需要进行增强处理的配置类。本文将深入分析增强处理的原理。
处理过程
本文的分析内容对应到代码就是enhanceConfigurationClasses中的一下这部分:
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);
// 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()));
}
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#enhance
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;
}
如果方法传入的configClass是 EnhancedConfiguration 类型,则说明已经被增强过,直接返回。如果不是的话,则通过newEnhancer方法,得到一个 Enhancer 对象,然后通过createClass方法,进行处理,并将结果返回。
先查看newEnhancer方法的处理逻辑。
// org.springframework.context.annotation.ConfigurationClassEnhancer#newEnhancer
/**
* Creates a new CGLIB { @link Enhancer} instance.
*/
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(configSuperClass);
enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
enhancer.setUseFactory(false);
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
enhancer.setCallbackFilter(CALLBACK_FILTER);
enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
return enhancer;
}
这里可以看出,Spring 对配置类的增强使用的是 CGLIB 的方式。这里给新创建的enhancer设置了一些信息,这些信息先留个印象,之后遇到了我们再回过来介绍。
创建好的enhancer会作为参数传入createClass方法中,我们进入createClass方法查看代码。
// org.springframework.context.annotation.ConfigurationClassEnhancer#createClass
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创建了增强的子类,在返回子类之前,通过 Enhancer 的registerStaticCallbacks方法为这个子类注册回调接口。常量CALLBACKS包含注册的回调接口。
// org.springframework.context.annotation.ConfigurationClassEnhancer#CALLBACKS
private static final Callback[] CALLBACKS = new Callback[] {
new BeanMethodInterceptor(),
new BeanFactoryAwareMethodInterceptor(),
NoOp.INSTANCE
};
下面我们逐个来看 BeanMethodInterceptor 和 BeanFactoryAwareMethodInterceptor 方法拦截器。
BeanMethodInterceptor
先看 BeanMethodInterceptor 拦截器,方法拦截器会根据isMatch方法筛选要拦截的方法。
// org.springframework.context.annotation.ConfigurationClassEnhancer.BeanMethodInterceptor#isMatch
@Override
public boolean isMatch(Method candidateMethod) {
return (candidateMethod.getDeclaringClass() != Object.class &&
!BeanFactoryAwareMethodInterceptor.isSetBeanFactory(candidateMethod) &&
BeanAnnotationHelper.isBeanAnnotated(candidateMethod));
}
由上面的代码可知,BeanMethodInterceptor 拦截的是有返回值、不是 BeanFactoryAware 接口的setBeanFactory方法,且被@Bean注解标记的方法。
拦截器的主要逻辑在它的intercept方法中。
代码量比较大,下面开始分析。
准备工作
ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
首先,通过getBeanFactory方法,从增强类实例中获取 BeanFactory 容器,进入方法查看源码。
private ConfigurableBeanFactory getBeanFactory(Object enhancedConfigInstance) {
Field field = ReflectionUtils.findField(enhancedConfigInstance.getClass(), BEAN_FACTORY_FIELD);
Assert.state(field != null, "Unable to find generated bean factory field");
Object beanFactory = ReflectionUtils.getField(field, enhancedConfigInstance);
Assert.state(beanFactory != null, "BeanFactory has not been injected into @Configuration class");
Assert.state(beanFactory instanceof ConfigurableBeanFactory,
"Injected BeanFactory is not a ConfigurableBeanFactory");
return (ConfigurableBeanFactory) beanFactory;
}
从源码中可以看出,容器对象就是增强配置类的$$beanFactory字段,这个字段是在创建增强类型的时候添加的,赋值的方式可以参考后文介绍的 BeanFactoryAwareMethodInterceptor 拦截器的逻辑。
再回到intercept方法中。
String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);
接下来,获取 BeanAnnotationHelper 的determineBeanNameFor方法,获取了被@``Bean标记的方法对应的 Bean 名称。获取的代码如下:
// org.springframework.context.annotation.BeanAnnotationHelper#determineBeanNameFor
public static String determineBeanNameFor(Method beanMethod) {
String beanName = beanNameCache.get(beanMethod);
if (beanName == null) {
// By default, the bean name is the name of the @Bean-annotated method
beanName = beanMethod.getName();
// Check to see if the user has explicitly set a custom bean name...
AnnotationAttributes bean =
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);
}
return beanName;
}
默认情况下,会把方法名作为 Bean 的名称,如果@``Bean注解的name属性设置了值,则将其作为 Bean 的名称。
再看intercept方法中后续的代码。
// Determine whether this bean is a scoped-proxy
if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
beanName = scopedBeanName;
}
}
如果方法通过@``Scope指定了作用域代理,则对它的 Bean 名称进行处理。
接着往下看。
增强 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
return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
}
}
在if判断条件中,会根据 Bean 名称判断容器中是否有对应的 Bean 以及其对应的 FactoryBean 工厂 Bean,如果都有的话,则说明当前的 Bean 是一个实现了 FactoryBean 的类型。对于符合条件的 Bean,会从容器中获取其对应的 FactoryBean,也就是工厂实例,对其进行增强。
在增强之前,还会判断这个工厂实例是不是 ScopedProxyFactoryBean 类型,如果是的话,则跳过增强的逻辑。最终,增强的工作是通过enhanceFactoryBean方法完成的。
我们进入enhanceFactoryBean方法查看源码。
// org.springframework.context.annotation.ConfigurationClassEnhancer.BeanMethodInterceptor#enhanceFactoryBean
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());
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.");
}
return createInterfaceProxyForFactoryBean(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.");
}
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);
}
这个方法的逻辑比较清晰。首先会判断类定义和getObject方法的定义,如果二者至少有一个是final修饰的,那么就会进入第一个if语句块。
进入后,会判断@``Bean方法的返回值,是不是一个接口类型。如果是的话,就通过createInterfaceProxyForFactoryBean方法创建动态代理并返回,如果不是的话,则直接返回factoryBean。
这里我们进入createInterfaceProxyForFactoryBean方法查看一下细节。
// org.springframework.context.annotation.ConfigurationClassEnhancer.BeanMethodInterceptor#createInterfaceProxyForFactoryBean
private Object createInterfaceProxyForFactoryBean(final Object factoryBean, Class<?> interfaceType,
final ConfigurableBeanFactory beanFactory, final String beanName) {
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);
});
}
可以看到,这里创建代理使用的是JDK动态代理的方式,如果当前调用的方法是getObject方法且参数为空,那么则返回根据 Bean 名称从容器中获取的 Bean 实例。
回到enhanceFactoryBean方法中,如果最开始的if判断条件不成立,则通过createCglibProxyForFactoryBean方法创建动态代理,从名称可以看出,这里使用的是CGLIB的方式。
// org.springframework.context.annotation.ConfigurationClassEnhancer.BeanMethodInterceptor#createCglibProxyForFactoryBean
private Object createCglibProxyForFactoryBean(final Object factoryBean,
final ConfigurableBeanFactory beanFactory, final String beanName) {
Enhancer enhancer = new Enhancer();
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();
Object fbProxy = null;
if (objenesis.isWorthTrying()) {
try {
fbProxy = objenesis.newInstance(fbClass, enhancer.getUseCache());
}
catch (ObjenesisException ex) {
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 (Throwable ex) {
throw new IllegalStateException("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) {
return beanFactory.getBean(beanName);
}
return proxy.invoke(factoryBean, args);
});
return fbProxy;
}
这里的代码量虽然相对较多,但是代理的逻辑跟前面通过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()));
}
return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
}
判断条件中调用了isCurrentlyInvokedFactoryMethod方法,我们查看其判断逻辑。
// org.springframework.context.annotation.ConfigurationClassEnhancer.BeanMethodInterceptor#isCurrentlyInvokedFactoryMethod
private boolean isCurrentlyInvokedFactoryMethod(Method method) {
Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod();
return (currentlyInvoked != null && method.getName().equals(currentlyInvoked.getName()) &&
Arrays.equals(method.getParameterTypes(), currentlyInvoked.getParameterTypes()));
}
这里判断了给定的方法,是否是配置类中的工厂方法,且工厂方法就是当前的方法。如果是,则调用父类中的方法创建实例。
最后,如果当前调用的方法对应的 Bean 名称不能在 Spring 容器中找到 FactoryBean,而且也没有定义工厂方法,则会执行resolveBeanReference方法。
return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
进入resolveBeanReference方法。
这个方法虽然很长,但是仔细看就会发现逻辑很简单,就是使用 Bean 的名称,从容器中获取实例。
至此,BeanMethodInterceptor 的逻辑就介绍完了,下面看 BeanFactoryAwareMethodInterceptor。
BeanFactoryAwareMethodInterceptor
先看isMatch方法。
// org.springframework.context.annotation.ConfigurationClassEnhancer.BeanFactoryAwareMethodInterceptor#isMatch
@Override
public boolean isMatch(Method candidateMethod) {
return isSetBeanFactory(candidateMethod);
}
public static boolean isSetBeanFactory(Method candidateMethod) {
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#intercept
@Override
@Nullable
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;
}
首先会判断增强类中是否存在名称为$$beanFactory的字段(常量BEAN_FACTORY_FIELD的值),如果存在的话,就会为该字段赋值,这样,增强后的配置类就能回去到 BeanFactory 容器了。
这个$$beanFactory是怎么来的呢?
在之前介绍过的newEnhancer方法创建增强类的时候,有这样一行代码:
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
这里给增强类设置了一个策略,在 BeanFactoryAwareGeneratorStrategy 中对配置类进行增强时,添加了$$beanFactory字段。
// org.springframework.context.annotation.ConfigurationClassEnhancer.BeanFactoryAwareGeneratorStrategy#transform
@Override
protected ClassGenerator transform(ClassGenerator cg) throws Exception {
ClassEmitterTransformer transformer = new ClassEmitterTransformer() {
@Override
public void end_class() {
declare_field(Constants.ACC_PUBLIC, BEAN_FACTORY_FIELD, Type.getType(BeanFactory.class), null);
super.end_class();
}
};
return new TransformingClassGenerator(cg, transformer);
}
最后的部分
到这里,enhance方法的全部逻辑就都介绍完了,我们再回到最初调用enhance方法的部分。
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()));
}
beanDef.setBeanClass(enhancedClass);
}
}
在for循环中,每获得一个配置类的增强类之后,就会将其对应的 BeanDefinition 类型信息设置为增强类型。
总结
本文分析了postProcessBeanFactory处理方法调用的enhanceConfigurationClasses方法对配置类进行增强处理的后半部分流程,也就是对配置类进行增强的逻辑。