原创

解析Spring中Configuration和Component区别

我们在往spring中添加组件时,会用到@Configuration,@Component 等注解。这些注解有什么区别,下面我们来一探究竟。
还是老样子,我们源码研究加例子。

@Configuration和@Component的表面区别

Configuration和Component注解的定义

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

}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {

    /**
     * The value may indicate a suggestion for a logical component name,
     * to be turned into a Spring bean in case of an autodetected component.
     * @return the suggested component name, if any (or empty String otherwise)
     */
    String value() default "";

}

我们通过对比发现@Configuration定义的时候竟然用到了@Component注解

@Configuration和@Component本质区别

我们这里先讲结论,不想看的小伙伴可以直接记住结论即可
,接着我们将带着结论来验证是否正确。
首先我们应该明白上面提到的注解都时只能用于类的,而不能修饰方法或者字段。
结论
@Configuration和@Component修饰的类都是配置,但是使用@Configuration修饰的类最后产生的类是一个CGlib代理类,这个类里面的方法都会得到增强

代码验证

这里省略了spring环境的搭建
在Config,java中分别使用 @Configuration@Component注解来看现象
People.java

public class People {
    private String name;
    private  Car car ;
    //省略getter/setter
}

Car.java

    public class Car {
    }

Config.java

@Configuration//第一次使用@Configuration,第二次我们将这里换成@Component注解
public class Config {
    @Bean
    public Car car(){
        System.out.println("Config.car");
        return  new Car();
    }
    @Bean
    public People people(){
        System.out.println("Config.people");
        People people = new People();
        people.setCar(car());
        return people;
    }
}

Main.java

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Config.class);
        People people = app.getBean(People.class);
        Car peopleCar = people.getCar();
        Car car = app.getBean(Car.class);
        System.out.println(peopleCar == car ? "同一个car":"不同的car");
    }
}

运行结果:
第一次使用@Configuration

Config.car
Config.people
同一个car

第二次使用@Component

Config.car
Config.people
Config.car
不同的car

通过对比我们发现使用@Configuration注解两次的car是同一个,而使用@Component注解时,使用的竟然不是同一个car.
结论我已经告诉大家了,使用@Configuration注解时,,生成的代理类会对里面的方法进行拦截,因此第二次取car时,直接是在容器中取出来的,而不是new的。
延伸思考:当配置类使用@Controller,@Service,@Repository时是什么情况?
和@Component是一样的
在Spring2.5引入了更多典型化注解(stereotype annotations):
@Component、@Service和@Controller,
其中@Component是所有受Spring管理组件的通用形式:而@Repository、@Service和@Controller则是@Component的细化。用来表示更具体的用例(例如,分别对应了持久化息、服务运和表现层)也就是说。你能用@Component来注解你的组件类。
但如果用@Repositary、@Service或QController来注解它们。也许你的类也许能更好地被工具处理,或与切面进行关联
例如,这些典型化注解可以成为理想的切入点目标。
当然,在SoringFrameWork以后的版本中。
@Repository@Service程Controller也许还能携带更多语义!
如此一来有如果你正在考虑服务层中是该用@Component还是@Service时那@Service显然是更好的选择。
同样的,就像前面所说的那样。@Repository已经能在持久化是中进行异常终换时被作为标记使用了.

解密

ConfigurationClassPostProcessor#enhanceConfigurationClasses

public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
        Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
        for (String beanName : beanFactory.getBeanDefinitionNames()) {
            BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
            //判断是否加了Configuration的类,如果是就加到configBeanDefs中,
            //加了这个注解的类就叫做完整配置类
            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.isWarnEnabled() && beanFactory.containsSingleton(beanName))
                    logger.warn("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);
            }
        }
        // nothing to enhance -> return immediately
        if (configBeanDefs.isEmpty()) 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
                // 设置用户指定bean类的增强子类
                Class<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader);
                if (configClass != null) {
                    // enhance增强,生成代理类,具体怎样增强就在enhance方法中
                    Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
                    if (configClass != enhancedClass) {
                        if (logger.isDebugEnabled())
                            logger.debug(String.format("Replacing bean definition '%s' existing class '%s' with " +
                                    "enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
                        //将代理类放到BeanDefinition中,我们用Class来描述java类,用Spring中的bean用什么描述呢?
                        //答案就是BeanDefinition
                        beanDef.setBeanClass(enhancedClass);
                    }
                }
            }
            catch (Throwable ex) {
                throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
            }
        }
    }

ConfigurationClassUtils中用来判断是否是增强配置类的方法

    /**
     * Determine whether the given bean definition indicates a full {@code @Configuration}
     * class, through checking {@link #checkConfigurationClassCandidate}'s metadata marker.
     */
     //判断是否是增强配置类
    public static boolean isFullConfigurationClass(BeanDefinition beanDef) {
        return CONFIGURATION_CLASS_FULL.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE));
    }

    /**
     * Determine whether the given bean definition indicates a lite {@code @Configuration}
     * class, through checking {@link #checkConfigurationClassCandidate}'s metadata marker.
     */
     //判断是否是精简配置类
    public static boolean isLiteConfigurationClass(BeanDefinition beanDef) {
        return CONFIGURATION_CLASS_LITE.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE));
    }

在该类中我们发现,标注了 Component ComponentScan Import ImportResource注解的类为精简配置类

static {
        candidateIndicators.add(Component.class.getName());
        candidateIndicators.add(ComponentScan.class.getName());
        candidateIndicators.add(Import.class.getName());
        candidateIndicators.add(ImportResource.class.getName());
    }
public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
        return metadata.isAnnotated(Configuration.class.getName());
    }

public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
        // Do not consider an interface or an annotation...
        if (metadata.isInterface()) return false;

        // Any of the typical annotations found?
        //遍历candidateIndicators看看是否存在里面的注解
        for (String indicator : candidateIndicators) if (metadata.isAnnotated(indicator)) return true;

        // Finally, let's look for @Bean methods...
        try {
            return metadata.hasAnnotatedMethods(Bean.class.getName());
        }
        catch (Throwable ex) {
            if (logger.isDebugEnabled())
                logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex);
            return false;
        }
    }

查看具体是怎样增强的

我们回到 Class<?> enhancedClass = 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.isDebugEnabled()) logger.debug(String.format("Successfully enhanced %s; enhanced class name is: %s",
                configClass.getName(), enhancedClass.getName()));
        return enhancedClass;
    }

private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(configSuperClass); //将配置类设置成父类,cglib基于继承
    enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class}); //将其的接口设置为EnhancedConfiguration加强功能
    enhancer.setUseFactory(false);
    enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
    enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
    // 加强点 BeanMethodInterceptor   BeanFactoryAwareMethodInterceptor  内部执行 intercept(),在方法前后进行拦截
    enhancer.setCallbackFilter(CALLBACK_FILTER);//这里是重点,也就是方法拦截器的回调方法
    enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
    return enhancer;
}

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;
}
//CALLBACK_FILTER主要为 BeanMethodInterceptor 和 BeanFactoryAwareMethodInterceptor,都是私有静态内部类。
private static final Callback[] CALLBACKS = new Callback[] {
    new BeanMethodInterceptor(),//逻辑
    new BeanFactoryAwareMethodInterceptor(),//设置属性 $$beanFactory
    NoOp.INSTANCE

};
我们点到BeanMethodInterceptor中,查看具体的实现

        public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
                    MethodProxy cglibMethodProxy) throws Throwable {

            ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
            String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);

            // Determine whether this bean is a scoped-proxy
            Scope scope = AnnotatedElementUtils.findMergedAnnotation(beanMethod, Scope.class);
            if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) {
                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);
                // It is a candidate FactoryBean - go ahead with enhancement
                if (factoryBean instanceof ScopedProxyFactoryBean) {
                    // Scoped proxy factory beans are a special case and should not be further proxied
                }
                else return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
            }
//检查给定的方法是否对应于容器当前调用的工厂方法。比较方法名和参数类型只是为了解决协变返回类型的潜在问题(目前只知道发生在Groovy类上)。--来自有道翻译。。。
            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.isWarnEnabled() &&
                        BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType()))
                    logger.warn(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()));
                //invoke方法调用的对象没有增强过,invokeSuper方法调用的对象已经是增强了的,
                // 所以会再走一遍 MyMethodInterceptor的 interceptor方法,
                // 如果是个拦截器链条,就会重新在走一次拦截器链

                return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
            }
        //在容器中获取bean
            return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
        }

总结

1.当一个类上面加了注解 @Configuration 时候,初始化ApplicationContext 的时候, invokeBeanDefinitionRegistryPostProcessors 方法解析此类,则从map里面遍历 BeanDefinition ,判断当前BeanDefinition 的map里面的属性key -configurationClass 值是full还是lite,如果为null,则继续解析,如果加了@Configuration 的值设置为full;
2.接下来 调用 invokeBeanFactoryPostPorcessors ,此方法遍历所有的BeanFactoryPostPorcessors(包括自定义的可以修改spring工厂) 在遍历中会调用到enhanceConfigurationClass ,此方法中获取regist里面的所有类,遍历判断 isfullConfigurationClass 是否为full,若为full,存到一个LinkedHashMap 中,为lite则不存,若map为null ,则返回之后直接new 对象;若map 不为null,则去完成cglib代理的实现。
3.cglib代理的实现:调用enhance() ,判断该类是否实现EnhancedConfiguration ,完成代理则会让该类去实现此接口;若没有被代理则去实现代理 enhancer.create()创建代码对象。
4.拦截器方法中执行增强逻辑,判断当前线程调用的方法和动态代理调用的方法名称是否一致,一致的说明是初次实例化调用,invoke调用父类方法进行实例化,不一致说明不是初次示例化调用,从BeanFactory中获取。
因此使用@Configuration修饰的类最后产生的类是一个CGlib代理类,这个类里面的方法会得到增强。这就是区别

正文到此结束(点击广告是对作者最大的支持)