@Configuration 注解也是 Spring 组件注解的一种,通过普通的 Bean 扫描也可以扫描到 @Configuration。
@Configuration 注解注册到 Spring 中的 Bean 是一个 CGLIB 代理的 Bean,而不是原始 Bean,这一点和 @Component 不一样,@Component 注册到 Spring 容器中的还是原始 Bean。
一个问题来了,@Configuration 标记的类为什么注册到 Spring 容器之后就变成了代理对象了呢?闭着眼睛大家也能猜到,肯定是为了通过代理来增强其功能,那么究竟增强什么功能呢?接下来我们通过源码分析来和小伙伴们梳理一下这里的条条框框。
2. 源码分析
要理解这个问题,首先得结合我们前面的文章@Configuration 注解的 Full 模式和 Lite 模式!,在该文中,松哥提到了 @Configuration 模式分为了 Full 模式和 Lite 模式,所以,对于 @Configuration 注解的处理,在加载的时候,就需要首先区分出来是 Full 模式还是 Lite 模式。
@Override publicvoidpostProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry){ int registryId = System.identityHashCode(registry); if (this.registriesPostProcessed.contains(registryId)) { thrownew IllegalStateException( "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); } if (this.factoriesPostProcessed.contains(registryId)) { thrownew IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + registry); } this.registriesPostProcessed.add(registryId); processConfigBeanDefinitions(registry); }
这个方面前面的代码主要是为了确保该方法执行一次,我们就不多说了。关键在于最后的 processConfigBeanDefinitions 方法,这个方法就是用来决策配置类是 Full 模式还是 Lite 模式的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
publicvoidprocessConfigBeanDefinitions(BeanDefinitionRegistry registry){ List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); String[] candidateNames = registry.getBeanDefinitionNames(); for (String beanName : candidateNames) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) { if (logger.isDebugEnabled()) { logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); } } elseif (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } } //省略。。。 }
我省略了其他代码,大家看,这个方法中,会首先根据 beanName 取出来 BeanDefinition,然后判断 BeanDefinition 中是否包含 ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE 属性,这个属性上记录了当前配置类是 Full 模式还是 Lite 模式,不同模式将来的处理方案肯定也是不同的。如果是第一次处理,显然 BeanDefinition 中并不包含该属性,因此就会进入到 ConfigurationClassUtils.checkConfigurationClassCandidate 方法中,正是在该方法中,判断当前配置类是 Full 模式还是 Lite 模式,并进行标记,checkConfigurationClassCandidate 方法的逻辑也挺长的,我这里挑出来跟我们感兴趣的部分:
Full 模式情况很简单,就是如果配置类上存在 @Configuration 注解,并且该注解的 proxyBeanMethods 属性值不为 false,那么就是 Full 模式,这个跟松哥在 @Configuration 注解的 Full 模式和 Lite 模式!一文中的介绍是一致的。
Lite 模式就情况多一些,首先 config!=null 就是说现在也存在 @Configuration 注解,但是 proxyBeanMethods 属性值此时为 false,那么就是 Lite 模式(proxyBeanMethods 属性值为 true 的话就进入到 if 分支中了)。
另外就是在 isConfigurationCandidate 方法中有一些判断逻辑去锁定是否为 Lite 模式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
staticbooleanisConfigurationCandidate(AnnotationMetadata metadata){ // Do not consider an interface or an annotation... if (metadata.isInterface()) { returnfalse; } // Any of the typical annotations found? for (String indicator : candidateIndicators) { if (metadata.isAnnotated(indicator)) { returntrue; } } // Finally, let's look for @Bean methods... return hasBeanMethods(metadata); }
这个方法的判断逻辑是这样:
首先注解要是标记的是接口,那就不能算是 Lite 模式。
遍历 candidateIndicators,判断当前类上是否包含这个 Set 集合中的注解,这个 Set 集合中的注解有四个,分别是 @Component、@ComponentScan、@Import、@ImportResource 四个,也就是,如果类上标记的是这四个注解的话,那么也按照 Lite 模式处理。
判断当前类中是否有 @Bean 标记的方法,如果有则按照 Lite 模式处理,否则就不是 Lite 模式。
/** * Prepare the Configuration classes for servicing bean requests at runtime * by replacing them with CGLIB-enhanced subclasses. */ @Override publicvoidpostProcessBeanFactory(ConfigurableListableBeanFactory beanFactory){ int factoryId = System.identityHashCode(beanFactory); if (this.factoriesPostProcessed.contains(factoryId)) { thrownew IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + beanFactory); } this.factoriesPostProcessed.add(factoryId); if (!this.registriesPostProcessed.contains(factoryId)) { // BeanDefinitionRegistryPostProcessor hook apparently not supported... // Simply call processConfigurationClasses lazily at this point then. processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory); } enhanceConfigurationClasses(beanFactory); beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory)); }
privatestaticclassBeanMethodInterceptorimplementsMethodInterceptor, ConditionalCallback{ @Override @Nullable 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 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); } } if (isCurrentlyInvokedFactoryMethod(beanMethod)) { return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs); } return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName); } }