第一眼看到这个题目,相信大家心里都会有一个想法:这不都是Spring的注解吗?有这两个注解的类,最后会封装成BeanDefinition,交给Spring管理,能有什么区别呢?首先,让我向您展示一个示例代码:AnnotationBean.javaimportlombok.Data;importorg.springframework.beans.factory.annotation.Qualifier;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation。配置;importorg.springframework.stereotype.Component;@Component//@ConfigurationpublicclassAnnotationBean{@Qualifier("innerBean1")@Bean()publicInnerBeaninnerBean1(){returnnewInnerBean();}@BeanpublicInnerBeanFactoryinnerBeanFactory(){InnerBeanFactory工厂=newInnerBeanFactory();factory.setInnerBean(innerBean1());返厂;}publicstaticclassInnerBean{}@DatapublicstaticclassInnerBeanFactory{privateInnerBeaninnerBean;}}AnnotationTest.java@Testvoidtest7(){AnnotationConfigApplicationContextapplicationContext=newAnnotationConfigA应用程序上下文(基础包);对象bean1=applicationContext.getBean("innerBean1");ObjectfactoryBean=applicationContext.getBean("innerBeanFactory");inthashCode1=bean1.hashCode();InnerBeaninnerBeanViaFactory=((InnerBeanFactory)factoryBean).getInnerBean();inthashCode2=innerBeanViaFactory.hashCode();Assertions.assertEquals(hashCode1,hashCode2);}大家可以先猜猜这个test7()的执行结果是成功还是失败?答案是失败。如果把AnnotationBean的注解从@Component改成@Configuration,那么test7()就会执行成功。为什么?通常Spring管理的bean不都是单例的吗?不着急,让笔者慢慢来~~~以下是来自Spring-source-5.2.8的两个注解的声明@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Indexedpublic@interfaceComponent{/***该值可能表示对逻辑组件名称的建议,*在自动检测到组件的情况下将其转换为Springbean。*@return建议的组件名称,如果有的话(否则为空字符串)*/Stringvalue()default"";}----------------------------------这是分界线--------------------------------@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic@interfaceConfiguration{/***显式指定与*{@code@Configuration}类关联的Springbean定义的名称.如果未指定(常见情况),将自动生成一个bean*名称。*
自定义名称仅在选择{@code@Configuration}类时适用*通过组件扫描或直接提供给*{@linkAnnotationConfigApplicationContext}。如果{@code@Configuration}类*注册为传统的XMLbean定义,则bean*元素的名称/id将优先。*@return明确的组件名称,如果有的话(否则为空字符串)*@seeAnnotationBeanNameGenerator*/@AliasFor(annotation=Component.class)Stringvalue()default"";/***指定是否应代理{@code@Bean}方法以强制执行*bean生命周期行为,例如即使在用户代码中直接调用{@code@Bean}方法时,也可以返回共享单例bean实例。此功能*需要方法拦截,通过运行时生成的CGLIB*子类实现,该子类具有配置类和*其方法不允许声明{@codefinal}等限制。*
默认值它是{@codetrue},允许通过直接*配置类中的方法调用进行“bean间引用”,以及外部调用*此配置的{@code@Bean}方法,例如来自另一个配置类。*如果不需要,因为每个特定配置的{@code@Bean}*方法都是自包含的,并且设计为容器使用的普通工厂方法,*将此标志切换为{@codefalse}以避免CGLIB子类处理。*
关闭bean方法拦截有效地处理{@code@Bean}*单独的方法,就像在非{@code@Configuration}类上声明时一样,*a.k.a.“@BeanLiteMode”(参见{@linkBean@Bean's文档})。因此*在行为上等同于移除{@code@Configuration}刻板印象。*@since5.2*/booleanproxyBeanMethods()defaulttrue;}从这两个注解的定义中,可能大家已经看到了一点端倪:@Configuration比@Component多了一个成员变量booleanproxyBeanMethods()默认值为true。从这个成员变量的注释中,我们可以看到一句话Specifywhether{@code@Bean}methodsshouldgetproxiedinordertoenforcebeanlifecyclebehavior,e.g.即使在用户代码中直接调用{@code@Bean}方法的情况下,也可以返回共享单例bean实例。其实从这句话中,我们可以初步得到我们想要的答案:在一个带有@Configuration注解的类中,一个带有@Bean注解的A方法显式调用另一个带有@Bean注解的方法,并返回一个共享的单例对象。下面我们从Spring源码实现的角度来看一下原理。从Spring源码实现来看,可以Spring作者在实现注解的时候,一般都是先收集分析,然后调用@Configuration是基于@Component实现的。在@Component的解析过程中,我们可以看到如下逻辑:=null&&!Boolean.FALSE.equals(config.get("proxyBeanMethods"))){beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE,CONFIGURATION_CLASS_FULL);}elseif(config!=null||isConfigurationCandidate(metadata)){beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE,CONFIGURATION_CLASS_LITE);}默认情况下,Spring在将带有@Configuration注解的类封装到BeanDefinition中时,会设置一个属性CONFIGURATION_CLASS_ATTRIBUTE,属性值为CONFIGURATION_CLASS_FULL,否则,只有@Component注解,则属性值为CONFIGURATION_CLASS_LITE(这个属性值很重要)。在@Component注解的调用过程中,有如下逻辑:org.springframework.context.annotation.ConfigurationClassPostProcessor#enhanceConfigurationClassesfor(StringbeanName:beanFactory.getBeanDefinitionNames()){......if((configClassAttr!=null||methodMetadata!=null)&&beanDefinstanceofAbstractBeanDefinition){......if(ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)){if(!(beanDefinstanceofAbstractBeanDefinition)){thrownewBeanDefinitionStoreException("无法增强@Configurationbean定义'"+beanName+"'因为它没有存储在AbstractBeanDefinition子类中");}elseif(logger.isInfoEnabled()&&beanFactory.containsSingleton(beanName)){logger.info("Cannotenhance@Configurationbeandefinition'"+beanName+"'sinceitssingletoninstancehasbeencreatedtooearly.Thetypicalcause"+“是一个带有BeanDefinitionRegistryPostProcessor的非静态@Bean方法”+“返回类型:考虑将此类方法声明为‘静态’。”);}configBeanDefs.put(beanName,(摘要BeanDefinition)beanDef);}}}if(configBeanDefs.isEmpty()){//nothingtoenhance->returnimmediatelyreturn;}ConfigurationClassEnhancerenhancer=newConfigurationClassEnhancer();...ifBeanDefinition的ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE属性如果值为ConfigurationClassUtils.CONFIGURATION_CLASS_FULL,则BeanDefinition对象将被添加到Map