日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

面试官:注解@Component,@Service是如何被解析的?

發(fā)布時(shí)間:2025/3/20 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 面试官:注解@Component,@Service是如何被解析的? 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
點(diǎn)擊上方?好好學(xué)java?,選擇?星標(biāo)?公眾號(hào)重磅資訊,干貨,第一時(shí)間送達(dá) 今日推薦:推薦19個(gè)github超牛逼項(xiàng)目!個(gè)人原創(chuàng)100W +訪問量博客:點(diǎn)擊前往,查看更多 來源: https://my.oschina.net/floor/blog/4325651 作者: 溫安適

前言

@Component和@Service都是工作中常用的注解,Spring如何解析?

1.@Component解析流程

找入口

Spring Framework2.0開始,引入可擴(kuò)展的XML編程機(jī)制,該機(jī)制要求XML Schema命名空間需要與Handler建立映射關(guān)系。

該關(guān)系配置在相對(duì)于classpath下的/META-INF/spring.handlers中。

如上圖所示 「ContextNamespaceHandler對(duì)應(yīng) context 分析的入口。」

「找核心方法」

瀏覽ContextNamespaceHandler

在parse中有一個(gè)很重要的注釋

?

// Actually scan for bean definitions and register them.

ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);

?

大意是:「ClassPathBeanDefinitionScanner#doScan是掃描BeanDefinition并注冊(cè)的實(shí)現(xiàn)」

ClassPathBeanDefinitionScanner 的源碼如下:

protected?Set<BeanDefinitionHolder>?doScan(String...?basePackages)?{Assert.notEmpty(basePackages,?"At?least?one?base?package?must?be?specified");Set<BeanDefinitionHolder>?beanDefinitions?=?new?LinkedHashSet<>();for?(String?basePackage?:?basePackages)?{//findCandidateComponents?讀資源裝換為BeanDefinitionSet<BeanDefinition>?candidates?=?findCandidateComponents(basePackage);for?(BeanDefinition?candidate?:?candidates)?{ScopeMetadata?scopeMetadata?=?this.scopeMetadataResolver.resolveScopeMetadata(candidate);candidate.setScope(scopeMetadata.getScopeName());String?beanName?=?this.beanNameGenerator.generateBeanName(candidate,?this.registry);if?(candidate?instanceof?AbstractBeanDefinition)?{postProcessBeanDefinition((AbstractBeanDefinition)?candidate,?beanName);}if?(candidate?instanceof?AnnotatedBeanDefinition)?{AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition)?candidate);}if?(checkCandidate(beanName,?candidate))?{BeanDefinitionHolder?definitionHolder?=?new?BeanDefinitionHolder(candidate,?beanName);definitionHolder?=AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata,?definitionHolder,?this.registry);beanDefinitions.add(definitionHolder);registerBeanDefinition(definitionHolder,?this.registry);}}}return?beanDefinitions; }

上邊的代碼,從方法名,猜測(cè):

「findCandidateComponents:從classPath掃描組件,并轉(zhuǎn)換為備選BeanDefinition,也就是要做的解析@Component的核心方法。」

「概要分析」

「findCandidateComponents在其父類ClassPathScanningCandidateComponentProvider 中。」

public?class?ClassPathScanningCandidateComponentProvider?implements?EnvironmentCapable,?ResourceLoaderAware?{ //省略其他代碼 public?Set<BeanDefinition>?findCandidateComponents(String?basePackage)?{if?(this.componentsIndex?!=?null?&&?indexSupportsIncludeFilters())?{return?addCandidateComponentsFromIndex(this.componentsIndex,?basePackage);}else?{return?scanCandidateComponents(basePackage);} } private?Set<BeanDefinition>?scanCandidateComponents(String?basePackage)?{Set<BeanDefinition>?candidates?=?new?LinkedHashSet<>();try?{String?packageSearchPath?=?ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX?+resolveBasePackage(basePackage)?+?'/'?+?this.resourcePattern;Resource[]?resources?=?getResourcePatternResolver().getResources(packageSearchPath);//省略部分代碼for?(Resource?resource?:?resources)?{//省略部分代碼if?(resource.isReadable())?{try?{MetadataReader?metadataReader?=?getMetadataReaderFactory().getMetadataReader(resource);if?(isCandidateComponent(metadataReader))?{ScannedGenericBeanDefinition?sbd?=?new?ScannedGenericBeanDefinition(metadataReader);sbd.setSource(resource);if?(isCandidateComponent(sbd))?{candidates.add(sbd);//省略部分代碼}}catch?(IOException?ex)?{//省略部分代碼?}return?candidates; } }

「findCandidateComponents大體思路如下:」

  • String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX resolveBasePackage(basePackage) + '/' + this.resourcePattern; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?將package轉(zhuǎn)化為ClassLoader類資源搜索路徑packageSearchPath,例如:com.wl.spring.boot轉(zhuǎn)化為classpath*:com/wl/spring/boot/**/*.class

  • Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); ?加載搜素路徑下的資源。

  • isCandidateComponent 判斷是否是備選組件

  • candidates.add(sbd); 添加到返回結(jié)果的list

  • ClassPathScanningCandidateComponentProvider#isCandidateComponent其源碼如下:

    protected?boolean?isCandidateComponent(MetadataReader?metadataReader)?throws?IOException?{//省略部分代碼for?(TypeFilter?tf?:?this.includeFilters)?{if?(tf.match(metadataReader,?getMetadataReaderFactory()))?{return?isConditionMatch(metadataReader);}}return?false; }

    includeFilters由registerDefaultFilters()設(shè)置初始值,有@Component,沒有@Service啊?

    protected?void?registerDefaultFilters()?{this.includeFilters.add(new?AnnotationTypeFilter(Component.class));ClassLoader?cl?=?ClassPathScanningCandidateComponentProvider.class.getClassLoader();try?{this.includeFilters.add(new?AnnotationTypeFilter(((Class<??extends?Annotation>)?ClassUtils.forName("javax.annotation.ManagedBean",?cl)),?false));logger.trace("JSR-250?'javax.annotation.ManagedBean'?found?and?supported?for?component?scanning");}catch?(ClassNotFoundException?ex)?{//?JSR-250?1.1?API?(as?included?in?Java?EE?6)?not?available?-?simply?skip.}try?{this.includeFilters.add(new?AnnotationTypeFilter(((Class<??extends?Annotation>)?ClassUtils.forName("javax.inject.Named",?cl)),?false));logger.trace("JSR-330?'javax.inject.Named'?annotation?found?and?supported?for?component?scanning");}catch?(ClassNotFoundException?ex)?{//?JSR-330?API?not?available?-?simply?skip.} }

    Spring如何處理@Service的注解的呢????

    「2.查文檔找思路」

    查閱官方文檔,下面這話:

    https://docs.spring.io/spring/docs/5.0.17.RELEASE/spring-framework-reference/core.html#beans-meta-annotations

    ?

    @Component is a generic stereotype for any Spring-managed component. @Repository, @Service, and @Controller are specializations of @Component

    ?

    大意如下:

    @Component是任何Spring管理的組件的通用原型。@Repository、@Service和@Controller是派生自@Component。

    @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented //?@Service?派生自@Component @Component public?@interface?Service?{/***?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)*/@AliasFor(annotation?=?Component.class)String?value()?default?"";}

    @Component是@Service的元注解,Spring 大概率,在讀取@Service,也讀取了它的元注解,并將@Service作為@Component處理。公眾號(hào)搜索,[Java學(xué)習(xí)之道],回復(fù)'福利',3T資料等你來拿!

    3. 探尋@Component派生性流程

    回顧C(jī)lassPathScanningCandidateComponentProvider 中的關(guān)鍵的代碼片段如下:

    private?Set<BeanDefinition>?scanCandidateComponents(String?basePackage)?{//省略其他代碼MetadataReader?metadataReader???=getMetadataReaderFactory().getMetadataReader(resource);??if(isCandidateComponent(metadataReader)){//....}????????? } public?final?MetadataReaderFactory?getMetadataReaderFactory()?{if?(this.metadataReaderFactory?==?null)?{this.metadataReaderFactory?=?new?CachingMetadataReaderFactory();}return?this.metadataReaderFactory; }

    「1. 確定metadataReader」

    CachingMetadataReaderFactory繼承自 SimpleMetadataReaderFactory,就是對(duì)SimpleMetadataReaderFactory加了一層緩存。

    其內(nèi)部的SimpleMetadataReaderFactory#getMetadataReader 為:

    public?class?SimpleMetadataReaderFactory?implements?MetadataReaderFactory?{@Override public?MetadataReader?getMetadataReader(Resource?resource)?throws?IOException?{return?new?SimpleMetadataReader(resource,?this.resourceLoader.getClassLoader()); }}

    這里可以看出

    「MetadataReader metadataReader =new SimpleMetadataReader(...);」

    「2.查看match方法找重點(diǎn)方法」

    AnnotationTypeFilter#matchself方法如下:

    @Override protected?boolean?matchSelf(MetadataReader?metadataReader)?{AnnotationMetadata?metadata?=?metadataReader.getAnnotationMetadata();return?metadata.hasAnnotation(this.annotationType.getName())?||(this.considerMetaAnnotations?&&?metadata.hasMetaAnnotation(this.annotationType.getName())); }

    「是metadata.hasMetaAnnotation法,從名稱看是處理元注解,我們重點(diǎn)關(guān)注」

    「逐步分析」

    找metadata.hasMetaAnnotation

    metadata=metadataReader.getAnnotationMetadata();

    metadataReader =new SimpleMetadataReader(...)

    metadata= new SimpleMetadataReader#getAnnotationMetadata()

    //SimpleMetadataReader?的構(gòu)造方法 SimpleMetadataReader(Resource?resource,?@Nullable?ClassLoader?classLoader)?throws?IOException?{InputStream?is?=?new?BufferedInputStream(resource.getInputStream());ClassReader?classReader;try?{classReader?=?new?ClassReader(is);}catch?(IllegalArgumentException?ex)?{throw?new?NestedIOException("ASM?ClassReader?failed?to?parse?class?file?-?"?+"probably?due?to?a?new?Java?class?file?version?that?isn't?supported?yet:?"?+?resource,?ex);}finally?{is.close();}AnnotationMetadataReadingVisitor?visitor?=new?AnnotationMetadataReadingVisitor(classLoader);classReader.accept(visitor,?ClassReader.SKIP_DEBUG);this.annotationMetadata?=?visitor;//?(since?AnnotationMetadataReadingVisitor?extends?ClassMetadataReadingVisitor)this.classMetadata?=?visitor;this.resource?=?resource; }

    metadata=new SimpleMetadataReader(...)**.**getAnnotationMetadata()= new AnnotationMetadataReadingVisitor(。。)

    也就是說

    「metadata.hasMetaAnnotation=AnnotationMetadataReadingVisitor#hasMetaAnnotation」

    其方法如下:

    public?class?AnnotationMetadataReadingVisitor{//?省略部分代碼 @Override public?boolean?hasMetaAnnotation(String?metaAnnotationType)?{Collection<Set<String>>?allMetaTypes?=?this.metaAnnotationMap.values();for?(Set<String>?metaTypes?:?allMetaTypes)?{if?(metaTypes.contains(metaAnnotationType))?{return?true;}}return?false; } }

    邏輯很簡(jiǎn)單,就是判斷該注解的元注解在,在不在metaAnnotationMap中,如果在就返回true。

    這里面核心就是metaAnnotationMap,搜索AnnotationMetadataReadingVisitor類,沒有發(fā)現(xiàn)賦值的地方??!。

    查找metaAnnotationMap賦值

    回到SimpleMetadataReader 的方法,

    //這個(gè)accept方法,很可疑,在賦值之前執(zhí)行 SimpleMetadataReader(Resource?resource,?@Nullable?ClassLoader?classLoader)?throws?IOException?{ //省略其他代碼 AnnotationMetadataReadingVisitor?visitor?=?new?AnnotationMetadataReadingVisitor(classLoader); classReader.accept(visitor,?ClassReader.SKIP_DEBUG);this.annotationMetadata?=?visitor;}

    發(fā)現(xiàn)一個(gè)可疑的語句:classReader.accept。

    查看accept方法

    public?class?ClassReader?{//省略其他代碼 public?void?accept(..省略代碼){//省略其他代碼readElementValues(classVisitor.visitAnnotation(annotationDescriptor,?/*?visible?=?*/?true),currentAnnotationOffset,true,charBuffer); } }

    查看readElementValues方法

    public?class?ClassReader{//省略其他代碼 private?int?readElementValues(final?AnnotationVisitor?annotationVisitor,final?int?annotationOffset,final?boolean?named,final?char[]?charBuffer)?{int?currentOffset?=?annotationOffset;//?Read?the?num_element_value_pairs?field?(or?num_values?field?for?an?array_value).int?numElementValuePairs?=?readUnsignedShort(currentOffset);currentOffset?+=?2;if?(named)?{//?Parse?the?element_value_pairs?array.while?(numElementValuePairs--?>?0)?{String?elementName?=?readUTF8(currentOffset,?charBuffer);currentOffset?=readElementValue(annotationVisitor,?currentOffset?+?2,?elementName,?charBuffer);}}?else?{//?Parse?the?array_value?array.while?(numElementValuePairs--?>?0)?{currentOffset?=readElementValue(annotationVisitor,?currentOffset,?/*?named?=?*/?null,?charBuffer);}}if?(annotationVisitor?!=?null)?{annotationVisitor.visitEnd();}return?currentOffset; } }

    這里面的核心就是 ?annotationVisitor.visitEnd();

    確定annotationVisitor

    這里的annotationVisitor=AnnotationMetadataReadingVisitor#visitAnnotation

    源碼如下,注意這里傳遞了metaAnnotationMap!!

    public?class?AnnotationMetadataReadingVisitor{ @Override public?AnnotationVisitor?visitAnnotation(String?desc,?boolean?visible)?{String?className?=?Type.getType(desc).getClassName();this.annotationSet.add(className);return?new?AnnotationAttributesReadingVisitor(className,?this.attributesMap,this.metaAnnotationMap,?this.classLoader); } }

    「annotationVisitor=AnnotationAttributesReadingVisitor」

    查閱annotationVisitor.visitEnd()

    「annotationVisitor=AnnotationAttributesReadingVisitor#visitEnd()」

    public?class?AnnotationAttributesReadingVisitor{ @Override public?void?visitEnd()?{super.visitEnd();Class<??extends?Annotation>?annotationClass?=?this.attributes.annotationType();if?(annotationClass?!=?null)?{List<AnnotationAttributes>?attributeList?=?this.attributesMap.get(this.annotationType);if?(attributeList?==?null)?{this.attributesMap.add(this.annotationType,?this.attributes);}else?{attributeList.add(0,?this.attributes);}if?(!AnnotationUtils.isInJavaLangAnnotationPackage(annotationClass.getName()))?{try?{Annotation[]?metaAnnotations?=?annotationClass.getAnnotations();if?(!ObjectUtils.isEmpty(metaAnnotations))?{Set<Annotation>?visited?=?new?LinkedHashSet<>();for?(Annotation?metaAnnotation?:?metaAnnotations)?{recursivelyCollectMetaAnnotations(visited,?metaAnnotation);}if?(!visited.isEmpty())?{Set<String>?metaAnnotationTypeNames?=?new?LinkedHashSet<>(visited.size());for?(Annotation?ann?:?visited)?{metaAnnotationTypeNames.add(ann.annotationType().getName());}this.metaAnnotationMap.put(annotationClass.getName(),?metaAnnotationTypeNames);}}}catch?(Throwable?ex)?{if?(logger.isDebugEnabled())?{logger.debug("Failed?to?introspect?meta-annotations?on?"?+?annotationClass?+?":?"?+?ex);}}}} } }

    內(nèi)部方法recursivelyCollectMetaAnnotations 遞歸的讀取注解,與注解的元注解(讀@Service,再讀元注解@Component),并設(shè)置到metaAnnotationMap,也就是AnnotationMetadataReadingVisitor 中的metaAnnotationMap中。

    總結(jié)

    大致如下:

    ClassPathScanningCandidateComponentProvider#findCandidateComponents

  • 將package轉(zhuǎn)化為ClassLoader類資源搜索路徑packageSearchPath

  • 加載搜素路徑下的資源。

  • isCandidateComponent 判斷是否是備選組件。

    內(nèi)部調(diào)用的TypeFilter的match方法:

    AnnotationTypeFilter#matchself中metadata.hasMetaAnnotation處理元注解

    metadata.hasMetaAnnotation=AnnotationMetadataReadingVisitor#hasMetaAnnotation

  • 就是判斷當(dāng)前注解的元注解在不在metaAnnotationMap中。

    AnnotationAttributesReadingVisitor#visitEnd()內(nèi)部方法recursivelyCollectMetaAnnotations 遞歸的讀取注解,與注解的元注解(讀@Service,再讀元注解@Component),并設(shè)置到metaAnnotationMap

  • 添加到返回結(jié)果的list

  • 推薦文章
    • 面試官問:前后端分離項(xiàng)目,有什么優(yōu)缺點(diǎn)?我說:沒

    • 2020 年騰訊新增 20 億行代碼,鵝廠第一編程語言還是它

    • 通俗講解分布式鎖,看完不懂算我輸

    • 寫博客能月入10K?

    • 一款基于 Spring Boot 的現(xiàn)代化社區(qū)(論壇/問答/社交網(wǎng)絡(luò)/博客)

    更多項(xiàng)目源碼
    • 這或許是最美的Vue+Element開源后臺(tái)管理UI

    • 推薦一款高顏值的 Spring Boot 快速開發(fā)框架

    • 一款基于 Spring Boot 的現(xiàn)代化社區(qū)(論壇/問答/社交網(wǎng)絡(luò)/博客)

    • 13K點(diǎn)贊都基于 Vue+Spring 前后端分離管理系統(tǒng)ELAdmin,大愛

    • 想接私活時(shí)薪再翻一倍,建議根據(jù)這幾個(gè)開源的SpringBoot項(xiàng)目

    總結(jié)

    以上是生活随笔為你收集整理的面试官:注解@Component,@Service是如何被解析的?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。