Component和Configuration注解区别实例详解_第1页
Component和Configuration注解区别实例详解_第2页
Component和Configuration注解区别实例详解_第3页
Component和Configuration注解区别实例详解_第4页
Component和Configuration注解区别实例详解_第5页
已阅读5页,还剩6页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第Component和Configuration注解区别实例详解目录引言AnnotationBean.javaAnnotationTest.javaSpring-source-5.2.8两个注解声明增强逻辑

引言

第一眼看到这个题目,我相信大家都会脑子里面弹出来一个想法:这不都是Spring的注解么,加了这两个注解的类都会被最终封装成BeanDefinition交给Spring管理,能有什么区别?

首先先给大家看一段示例代码:

AnnotationBean.java

importlombok.Data;

importorg.springframework.beans.factory.annotation.Qualifier;

importorg.springframework.context.annotation.Bean;

importorg.springframework.context.annotation.Configuration;

importorg.springframework.stereotype.Component;

@Component

//@Configuration

publicclassAnnotationBean{

@Qualifier("innerBean1")

@Bean()

publicInnerBeaninnerBean1(){

returnnewInnerBean();

@Bean

publicInnerBeanFactoryinnerBeanFactory(){

InnerBeanFactoryfactory=newInnerBeanFactory();

factory.setInnerBean(innerBean1());

returnfactory;

publicstaticclassInnerBean{

@Data

publicstaticclassInnerBeanFactory{

privateInnerBeaninnerBean;

}

AnnotationTest.java

@Test

voidtest7(){

AnnotationConfigApplicationContextapplicationContext=newAnnotationConfigApplicationContext(BASE_PACKAGE);

Objectbean1=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两个注解声明

以下是摘自Spring-source-5.2.8的两个注解的声明

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Indexed

public@interfaceComponent{

*Thevaluemayindicateasuggestionforalogicalcomponentname,

*tobeturnedintoaSpringbeanincaseofanautodetectedcomponent.

*@returnthesuggestedcomponentname,ifany(oremptyStringotherwise)

Stringvalue()default"";

----------------------------------这是分割线-----------------------------------

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Component

public@interfaceConfiguration{

*ExplicitlyspecifythenameoftheSpringbeandefinitionassociatedwiththe

*{@code@Configuration}class.Ifleftunspecified(thecommoncase),abean

*namewillbeautomaticallygenerated.

*pThecustomnameappliesonlyifthe{@code@Configuration}classispicked

*upviacomponentscanningorsupplieddirectlytoan

*{@linkAnnotationConfigApplicationContext}.Ifthe{@code@Configuration}class

*isregisteredasatraditionalXMLbeandefinition,thename/idofthebean

*elementwilltakeprecedence.

*@returntheexplicitcomponentname,ifany(oremptyStringotherwise)

*@seeAnnotationBeanNameGenerator

@AliasFor(annotation=Component.class)

Stringvalue()default"";

*Specifywhether{@code@Bean}methodsshouldgetproxiedinordertoenforce

*beanlifecyclebehavior,e.g.toreturnsharedsingletonbeaninstanceseven

*incaseofdirect{@code@Bean}methodcallsinusercode.Thisfeature

*requiresmethodinterception,implementedthrougharuntime-generatedCGLIB

*subclasswhichcomeswithlimitationssuchastheconfigurationclassand

*itsmethodsnotbeingallowedtodeclare{@codefinal}.

*pThedefaultis{@codetrue},allowingfor'inter-beanreferences'viadirect

*methodcallswithintheconfigurationclassaswellasforexternalcallsto

*thisconfiguration's{@code@Bean}methods,e.g.fromanotherconfigurationclass.

*Ifthisisnotneededsinceeachofthisparticularconfiguration's{@code@Bean}

*methodsisself-containedanddesignedasaplainfactorymethodforcontaineruse,

*switchthisflagto{@codefalse}inordertoavoidCGLIBsubclassprocessing.

*pTurningoffbeanmethodinterceptioneffectivelyprocesses{@code@Bean}

*methodsindividuallylikewhendeclaredonnon-{@code@Configuration}classes,

*a.k.a."@BeanLiteMode"(see{@linkBean@Bean'sjavadoc}).Itistherefore

*behaviorallyequivalenttoremovingthe{@code@Configuration}stereotype.

*@since5.2

booleanproxyBeanMethods()defaulttrue;

}

从这两个注解的定义中,可能大家已经看出了一点端倪:@Configuration比@Component多一个成员变量booleanproxyBeanMethods()默认值是true.从这个成员变量的注释中,我们可以看到一句话

Specifywhether{@code@Bean}methodsshouldgetproxiedinordertoenforcebeanlifecyclebehavior,e.g.toreturnsharedsingletonbeaninstancesevenincaseofdirect{@code@Bean}methodcallsinusercode.

其实从这句话,我们就可以初步得到我们想要的答案了:在带有@Configuration注解的类中,一个带有@Bean注解的方法显式调用另一个带有@Bean注解的方法,返回的是共享的单例对象.下面我们从Spring源码实现角度来看看这中间的原理.

从Spring源码实现中可以得出一个规律,Spring作者在实现注解时,通常是先收集解析,再调用。@Configuration是基于@Component实现的,在@Component的解析过程中,我们可以看到下面一段逻辑:

org.springframework.context.annotation.ConfigurationClassUtils#checkConfigurationClassCandidate

MapString,Objectconfig=metadata.getAnnotationAttributes(Configuration.class.getName());

if(config!=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#enhanceConfigurationClasses

for(StringbeanName:beanFactory.getBeanDefinitionNames()){

......

if((configClassAttr!=null||methodMetadata!=null)beanDefinstanceofAbstractBeanDefinition){

......

if(ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)){

if(!(beanDefinstanceofAbstractBeanDefinition)){

thrownewBeanDefinitionStoreException("Cannotenhance@Configurationbeandefinition'"+

beanName+"'sinceitisnotstoredinanAbstractBeanDefinitionsubclass");

elseif(logger.isInfoEnabled()beanFactory.containsSingleton(beanName)){

("Cannotenhance@Configurationbeandefinition'"+beanName+

"'sinceitssingletoninstancehasbeencreatedtooearly.Thetypicalcause"+

"isanon-static@BeanmethodwithaBeanDefinitionRegistryPostProcessor"+

"returntype:Considerdeclaringsuchmethodsas'static'.");

configBeanDefs.put(beanName,(AbstractBeanDefinition)beanDef);

if(configBeanDefs.isEmpty()){

//nothingtoenhance-returnimmediately

return;

ConfigurationClassEnhancerenhancer=newConfigurationClassEnhancer();

......

如果BeanDefinition的ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE属性值为ConfigurationClassUtils.CONFIGURATION_CLASS_FULL,则该BeanDefinition对象会被加入到MapString,AbstractBeanDefinitionconfigBeanDefs容器中。

如果Spring发现该Map是空的,则认为不需要进行代理增强,立即返回;反之,则为该类(本文中,被代理类即为AnnotationBean,以下简称该类)创建代理。

所以如果该类的注解是@Component,调用带有@Bean注解的innerBean1()方法时,this对象为Spring容器中的真实单例对象,例如AnnotationBean@4149.

@Bean

publicInnerBeanFactoryinnerBeanFactory(){

InnerBeanFactoryfactory=newInnerBeanFactory();

factory.setInnerBean(innerBean1());

returnfactory;

}

那在上述方法中每调用一次innerBean1()方法时,势必会返回一个新创建的InnerBean对象。如果该类的注解为@Configuration时,this对象为Spring生成的AnnotationBean的代理对象,例如AnnotationBean$$EnhancerBySpringCGLIB$$90f8540c@4296,

增强逻辑

//Thecallbackstouse.Notethatthesecallbacksmustbestateless.

privatestaticfinalCallback[]CALLBACKS=newCallback[]{

newBeanMethodInterceptor(),

newBeanFactoryAwareMethodInterceptor(),

NoOp.INSTANCE

-----------------------------------这是分割线-------------------------------

*CreatesanewCGLIB{@linkEnhancer}instance.

privateEnhancernewEnhancer(ClassconfigSuperClass,@NullableClassLoaderclassLoader){

Enhancerenhancer=newEnhancer();

enhancer.setSuperclass(configSuperClass);

enhancer.setInterfaces(newClass[]{EnhancedConfiguration.class});

enhancer.setUseFactory(false);

enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);

enhancer.setStrategy(newBeanFactoryAwareGeneratorStrategy(classLoader));

enhancer.setCallbackFilter(CALLBACK_FILTER);

enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());

returnenhancer;

}

当在上述方法中调用innerBean1()时,ConfigurationClassEnhancer遍历3种回调方法判断当前调用应该使用哪个回调方法时,第一个回调类型匹配成功org.springframework.context.annotation.ConfigurationClassEnhancer.BeanMethodInterceptor#isMatch匹配过程如下所示:

@Override

publicbooleanisMatch(MethodcandidateMethod){

return(candidateMethod.getDeclaringClass()!=Object.class!BeanFactoryAwareMethodInterceptor.

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论