转载

Spring5源码解析7-ConfigurationClassPostProcessor (下)

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

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

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

ConfigurationClassPostProcessor#postProcessBeanFactory

调用时机

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

源码解析

//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) 方法中产生的,源码如下:

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); 方法源码:

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增强类。

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...

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

那具体做了哪些增强呢?

  1. 实现 EnhancedConfiguration 接口。这是一个空的标志接口,仅由Spring框架内部使用,并且由所有 @Configuration CGLIB子类实现,该接口继承了 BeanFactoryAware 接口。
  2. 设置了命名策略
  3. 设置生成器创建字节码的策略。 BeanFactoryAwareGeneratorStrategy 继承了cglib的 DefaultGeneratorStrategy ,其主要作用是为了让子类引入 BeanFactory 字段和设置 ClassLoader
  4. 设置增强 Callback
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相关源码。

@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 方法时,就会被以下代码所拦截:

//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 对象。

// 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文件:

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "spring-study/docs/classes");

源码注释GITHUB地址: https://github.com/shenjianen...

欢迎关注公众号,一起学习成长。

Spring5源码解析7-ConfigurationClassPostProcessor (下)

原文  https://segmentfault.com/a/1190000020633405
正文到此结束
Loading...