【Spring】@Configuration为什么会生成代理呢?

1  前言

首先说下为什么会产生这样的疑惑哈,最近在看Spring-retry的时候,发现:

其次我们再来看个现象,@Component 声明了一个Bean,内部有个单例AService,当我们调用两次 aService() 发现得到的对象不一样:

@Component
public class Demo {

    public class AService {
        public AService() {
            System.out.println("初始化AService");
        }
    }

    @Bean
    public AService aService() {
        return new AService();
    }

    @PostConstruct
    public void init() {
        AService a1 = aService();
        AService a2 = aService();
        System.out.println(a1);
        System.out.println(a2);
    }
}

那我们把@Component  换成 @Configuration 再来看看:

看见没构造器执行了一次,并且获得的都是同一个对象,其实就是因为生成了代理,方法被增强处理了,那么接下来我们就来看看是怎么回事。

2  源码分析

2.1  入口分析

我们说了是会生成代理的,生成代理的类就是 ConfigurationClassPostProcessor,那么它是什么时候入场的呢,我们来看看:

看类图发现它实现了 BeanDefinitionRegistryPostProcessor,而该接口又继承了 BeanFactoryPostProcessor 说明这个类是个BeanFactoyry的后置处理器哇,工厂后置处理是什么时候执行的呢,是不是就是在刷新的时候执行的?

那么是如何进到创建代理的,我画个图来理解一下:

如上图所示,就是 BeanFactory的后置处理,进入到我们的 ConfigurationClassPostProcessor类里,给@Configuration修饰的类创建代理的,那么具体是如何创建的,怎么创建我们细细往下看。

2.2  ConfigurationClassPostProcessor 的 postProcessBeanDefinitionRegistry

在看之前我们先看下 ConfigurationClassPostProcessor 中的 postProcessBeanDefinitionRegistry这个方法,这个方法是优先于 postProcessBeanFactory,它主要做什么呢?就是从注册进来的配置类(可能是Full模式,可能是Lite模式)里进一步派生bean定义。简而言之:收集到所有的BeanDefinition(后简称为bd)存储起来,包括@Import、@Component等等组件。并且做出标注:是Full模式的还是Lite模式的配置类(若非配置组件就不标注哦)。也是为postProcessBeanFactory做准备的。

/**
 * Derive further bean definitions from the configuration classes in the registry.
 */
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    // 生成一个id,防止重复执行
    int registryId = System.identityHashCode(registry);
    // 若重复就抛出异常
    if (this.registriesPostProcessed.contains(registryId)) {
        throw new IllegalStateException(
                "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
    }
    if (this.factoriesPostProcessed.contains(registryId)) {
        throw new IllegalStateException(
                "postProcessBeanFactory already called on this post-processor against " + registry);
    }
    // 表示此registry里的bd收集动作,已经做了  避免再重复收集此registry
    this.registriesPostProcessed.add(registryId);
    // 根据配置类,收集到所有的bd信息
    // 并且做出mark标注:是Full模式还是Lite模式,和很重要很重要
    processConfigBeanDefinitions(registry);
}

我们就不进去细看了哈,大概总结下什么情况下是Full模式、Lite模式。

  • Full模式:@Configuration注解并且proxyBeanMethods属性等于true
  • Lite模式:@Component,@ComponentScan,@Import,@ImportResource这几个注解的类(或者加了@Configuration注解但是proxyBeanMethods属性等于false)

2.3  ConfigurationClassPostProcessor 的 postProcessBeanFactory

完成了bd的收集和标记,那接下来就是我们要看的 postProcessBeanFactory,此方法的作用用一句话可概括为:为Full模式的Bean使用CGLIB做字节码提升,确保最终生成的是代理类实例放进容器内

/**
 * Prepare the Configuration classes for servicing bean requests at runtime
 * by replacing them with CGLIB-enhanced subclasses.
 */
@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);
    // 在执行postProcessBeanDefinitionRegistry方法的时就已经将
    // 这个id添加到registriesPostProcessed集合中了
    // 所以到这里就不会再重复执行配置类的解析了(解析@Import、@Bean等)
    if (!this.registriesPostProcessed.contains(factoryId)) {
        // BeanDefinitionRegistryPostProcessor hook apparently not supported...
        // Simply call processConfigurationClasses lazily at this point then.
        processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
    }
    // 这个方法就是为配置类创建代理   enhance是cglib代理创建的核心类
    enhanceConfigurationClasses(beanFactory);
    beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}

达到这一步之前,已经完成了bd的收集和标记。对bd进行实例化之前,针对于Full模式的配置类这步骤里会做增强处理,那就是enhanceConfigurationClasses(beanFactory)这个方法,我们继续看。

2.4  enhanceConfigurationClasses 方法

对一个BeanFactory进行增强,先查找配置类BeanDefinition,再根据Bean定义信息(元数据信息)来决定配置类是否应该被ConfigurationClassEnhancer增强。具体我们看看:

/**
 * Post-processes a BeanFactory in search of Configuration class BeanDefinitions;
 * any candidates are then enhanced by a {@link ConfigurationClassEnhancer}.
 * Candidate status is determined by BeanDefinition attribute metadata.
 * @see ConfigurationClassEnhancer
 */
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
    StartupStep enhanceConfigClasses = this.applicationStartup.start("spring.context.config-classes.enhance");
    // 收集哪些Bean 需要增强
    Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
    // 遍历当前BeanFactory中的 Bean定义,也就是要找@Configuration注解修饰的Bean
    for (String beanName : beanFactory.getBeanDefinitionNames()) {
        // 获取到Bean的定义
        BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);...// 如果是Full模式,才会放进来
        if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
            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);
        }
    }
    // 如果发现没有 bean需要增强就直接返回
    if (configBeanDefs.isEmpty() || NativeDetector.inNativeImage()) {
        // nothing to enhance -> return immediately
        enhanceConfigClasses.end();
        return;
    }
    // ConfigurationClassEnhancer 应该就是这个类创建增强的类
    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
        // 如果代理了@Configuration类,则始终代理目标类
        // 该属性和自动代理时是相关的
        beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
        // Set enhanced subclass of the user-specified bean class
        // CGLIB是给继承的方式实现代理,所以这里指定“父类”类型
        Class<?> configClass = beanDef.getBeanClass();
        // 做增强处理,返回enhancedClass就是一个增强过的子类
        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);
        }
    }
    enhanceConfigClasses.tag("classCount", () -> String.valueOf(configBeanDefs.keySet().size())).end();
}

大致的执行步骤就是:
(1)从BeanFactory拿出所有的bd信息,一个个判断如果是配置类并且是Full模式,就先存储起来,后面会对它做字节码提升。最终如果一个Full模式的配置类都没有的话,那直接return,此方法结束。否则继续
(2)对收集到的每一个 Full模式的配置类,使用ConfigurationClassEnhancer增强器进行字节码提升,生成一个CGLIB子类型并此处显示标注了AOP自动代理为:始终代理目标类
(3)把CGLIB生成的子类型设置到元数据里去:beanDef.setBeanClass(enhancedClass)。这样Spring在最后实例化Bean时,实际生成的是该代理类型的实例,从而达到代理/增强的目的

2.5  ConfigurationClassEnhancer 创建代理

通过生成一个CGLIB子类来增强@Configuration类与Spring容器进行交互,每个这样的@Bean方法都会被生成的子类所复写。这样子当遇到方法调用时,从而保证单例特性,获得相应的Bean。我们继续看代理的创建:

/**
 * Loads the specified class and generates a CGLIB subclass of it equipped with
 * container-aware callbacks capable of respecting scoping and other bean semantics.
 * @return the enhanced subclass
 */
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;
    }
    // 若没被代理过,就先调用newEnhancer()方法创建一个增强器Enhancer
    // 然后使用这个增强器,生成代理类字节码Class对象
    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;
}

可以看到是先给我们的熬代理的类,创建一个增强器newEnhancer(),然后调用createClass创建其代理,那我们来看下这两个方法。

2.5.1  newEnhancer 方法

创建一个新的CGLIB Enhancer实例,并且做好相应配置。

/**
 * Creates a new CGLIB {@link Enhancer} instance.
 */
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
    Enhancer enhancer = new Enhancer();
    // 设置被代理的类为其父类型 cglib就是继承的方式实现代理的
    enhancer.setSuperclass(configSuperClass);
    // 让代理类实现EnhancedConfiguration接口,这个接口继承了BeanFactoryAware接口
    enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
    // 设置生成的代理类不实现org.springframework.cglib.proxy.Factory接口
    enhancer.setUseFactory(false);
    // 设置代理类名称的生成策略:Spring定义的一个生成策略
    // 即你代理的名称中会有“BySpringCGLIB”字样
    enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
    enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
    // 设置拦截器/过滤器
    enhancer.setCallbackFilter(CALLBACK_FILTER);
    enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
    return enhancer;
}

2.5.2  createClass 方法

/**
 * Uses enhancer to generate a subclass of superclass,
 * ensuring that callbacks are registered for the new subclass.
 */
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是CGLIB的最核心API,而这里好像是spring内的,这是因为CGLIB在Spring内太常用了(强依赖),因此Spring索性就自己fork了一份代码过来。之前我们也讲过cglib的代理哈,大家可以去看看。通过这两个方法我们能看到:

  • 通过它增强的每个类都实现了EnhancedConfiguration接口,并且它还是BeanFactoryAware的子接口,这样Spring在创建代理类实例的时候会给注入BeanFactory
  • 使用SpringNamingPolicy策略来生成类名称。这就是解释了为何代理类的名你都能看到BySpringCGLIB字样
  • 那么对于代理最为重要的当属过滤器/拦截器org.springframework.cglib.proxy.Callback,它们是实现功能的核心

配置此增强器时设置了CALLBACK_FILTER共三个拦截器:

private static final Callback[] CALLBACKS = new Callback[] {
        new BeanMethodInterceptor(),
        new BeanFactoryAwareMethodInterceptor(),
        NoOp.INSTANCE
};

2.5.3  拦截器分析

上面的三个拦截器中,NoOp.INSTANCE代表什么都没做,因此我们只需要关注前两个。他俩都是MethodInterceptor接口的实现类,均实现了intercept()方法来做具体的拦截操作,我们就来看看。

2.5.3.1 BeanFactoryAwareMethodInterceptor 拦截器

看名字,表示的是BeanFactoryAware方法的拦截器,所以应该能猜到它拦截的是setBeanFactory(beanFactory)方法,我们看是不是:

@Override
// 当执行到setBeanFactory(xxx)方法时匹配成功
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()));
}

可以看到去匹配我们的setBeanFactory方法,isSetBeanFactory()判断方法做这么“复杂”主要是为了容错,“担心”你自己定义了一个名为setBeanFactory的方法而“搞错了”。那它到底做了什么事情呢,我们继续看:

@Override
@Nullable
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
    // 找到类里名为`$$beanFactory`的字段 注意这里的类是代理类
    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;
}

拦截的是setBeanFactory()方法的执行。所以这里的Method就代表的是setBeanFactory()方法,Object[] args的值是当前容器的BeanFactory工厂(注意理解这句话)实例。说白了其实就是给代理类设置 BeanFactory。

2.5.3.2 BeanMethodInterceptor 拦截器

我们看下它拦截的匹配规则:

/**
 *  匹配的规则:
 *  1、不是Object类的方法,看过cglib生成的代理类源码的知道 生成后会有toString等Object的方法
 *  2、不是setBeanFactory方法  这个理解的吧
 *  3、@Bean注解的方法
 *  其实也就是匹配@Bean注解标注的方法,快跟我们上边的困惑对上了哈
 */
public boolean isMatch(Method candidateMethod) {
    return (candidateMethod.getDeclaringClass() != Object.class &&
            !BeanFactoryAwareMethodInterceptor.isSetBeanFactory(candidateMethod) &&
            BeanAnnotationHelper.isBeanAnnotated(candidateMethod));
}

其实就是拦截@Bean标注的方法哈,我们继续看匹配到方法后的处理:

/**
 * Enhance a {@link Bean @Bean} method to check the supplied BeanFactory for the
 * existence of this bean object.
 * @throws Throwable as a catch-all for any exception that may be thrown when invoking the
 * super implementation of the proxied method i.e., the actual {@code @Bean} method
 */
@Override
@Nullable
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
            MethodProxy cglibMethodProxy) throws Throwable {
    // 获取到 BeanFactory 这里别有疑问哈,会不会获得的是空呢?不会的哈,BeanFactoryAware赋值的优先级还是比较高的
    ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
    // 根据当前方法获得一个bean的名字 默认就是我们的方法名字哈
    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.
    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);
        }
    }
    // 如果当前bean 是Spring人家正在创建的,就调用父类即被代理类实际方法生成bean实例,并注册到Spring容器中
    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()));
        }
        // 执行父类的方法生成Bean实例
        return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
    }
    // 走到这里说明此时的@Bean方法并不是由spring调用的,而是用户自身的代码显示调用的
    // 从BeanFactory中获取bean
    return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}

上边的这个方法暂时没看全,先放着哈,设计的东西比较多主要是bean的实例化过程以及bean注入的先后顺序都会形成不同的场景,就要根据不同的场景去做判断处理,就先看到这里哈。

3  @Component与@Configuration区别

@Configuration里其实就有@Component注解,最大的区别就是@Configuration会生成代理对象,@Bean的注解方法都会被代理增强保证单例。

4  小结

本节我们主要看了下@Configuration注解的类的代理创建时机和过程,要知道生成的代理类实现了BeanFactoryAware接口以及两个增强方法,分别是针对setBeanFactory的增强和@Bean注解方法的增强处理哈,有理解不对的地方欢迎指正哈。

posted @ 2023-04-21 09:46  酷酷-  阅读(411)  评论(0编辑  收藏  举报