Spring的成长-注解@Configuration 和 @Component

253 阅读2分钟

有迷茫,就会有困惑,有困惑,就有寻找答案的动力,尽管这一过程并不舒服,但是正如士兵突击里面许三多说的,人不能过的太舒服

前言

我一直有一个疑问,就是@Configuration 和 @Component有什么区别?他们都能将对象放入spring容器,那他们的区别是什么呢?

带着这个困惑,今天终于找到了答案

@Configuration 注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    String value() default "";
}

上一篇中,我有说过@Configuration是 @Component的派生注解,@Configuration包含@Component的所有功能,那他本身有什么特性呢?他是怎么实现将类放入Spring容器的呢,我们来看看

特性:

  • 配置类必须以类的形式提供(不能是工厂方法返回的实例),允许通过生成子类在运行时增强(cglib 动态代理)。
  • 配置类不能是 final 类(没法动态代理)。
  • 配置注解通常为了通过 @Bean 注解生成 Spring 容器管理的类,
  • 配置类必须是非本地的(即不能在方法中声明,不能是 private)。
  • 任何嵌套配置类都必须声明为static。
  • @Bean 方法可能不会反过来创建进一步的配置类(也就是返回的 bean 如果带有 @Configuration,也不会被特殊处理,只会作为普通的 bean)

从特性中我们发现,@Configuration将配置的类放入容器实际是通过动态代理来实现的,通过具体的代码查看 在 ConfigurationClassPostProcessor 中的 postProcessBeanFactory 方法中调用了下面的方法:

/**
 * 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) {
    Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<String, AbstractBeanDefinition>();
    for (String beanName : beanFactory.getBeanDefinitionNames()) {
        BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
        // 查询系统中所有带有@Configuration的bean定义
        if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {
            //省略部分代码
            //将@Configuration的bean定义放入MAP集合
            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);
            // 使用动态代理  增强后的类替换原有的类  cglib动态代理的方式,见下面代码
            Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
            if (configClass != enhancedClass) {
                //省略部分代码
                beanDef.setBeanClass(enhancedClass);
            }
        }
        catch (Throwable ex) {
            throw new IllegalStateException(
              "Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
        }
    }
}

cglib动态里生成增强类

private Enhancer newEnhancer(Class<?> superclass, ClassLoader classLoader) {
    Enhancer enhancer = new Enhancer();
    // cglib的动态代理是通过生成子类的形式
    enhancer.setSuperclass(superclass);
    enhancer.setInterfaces(new Class[]{ConfigurationClassEnhancer.EnhancedConfiguration.class});
    enhancer.setUseFactory(false);
    enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
    enhancer.setStrategy(new ConfigurationClassEnhancer.BeanFactoryAwareGeneratorStrategy(classLoader));
    enhancer.setCallbackFilter(CALLBACK_FILTER);
    enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
    return enhancer;
}

通过 cglib 代理的类在调用方法时,会通过 CallbackFilter 调用

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

private static final ConditionalCallbackFilter CALLBACK_FILTER = 
        new ConditionalCallbackFilter(CALLBACKS);

最终会匹配BeanMethodInterceptor 与BeanFactoryAwareMethodInterceptor类的 isMatch方法

public boolean isMatch(Method candidateMethod) {
    return BeanAnnotationHelper.isBeanAnnotated(candidateMethod);
}
public boolean isMatch(Method candidateMethod) {
    return candidateMethod.getName().equals("setBeanFactory") && candidateMethod.getParameterTypes().length == 1 && BeanFactory.class == candidateMethod.getParameterTypes()[0] && BeanFactoryAware.class.isAssignableFrom(candidateMethod.getDeclaringClass());
}

最终将@Bean修饰的对象放入Spring容器。

这里我们就十分清晰了@Configuration 的实现原理。

而@Component并没有使用动态代理,他是会直接将配置类放入spring容器。