京东一面:说说@Component,@Service等注解是如何被解析的?

沙海 2021年3月12日01:05:48杂谈 Java评论27字数 12481阅读41分36秒阅读模式
摘要

速读摘要

速读摘要文章源自JAVA秀-https://www.javaxiu.com/3727.html

在读取@Service,也读取了它的元注解,并将@Service作为@Component处理。}逻辑很简单,就是判断该注解的元注解在,在不在metaAnnotationMap中,如果在就返回true。文章源自JAVA秀-https://www.javaxiu.com/3727.html

原文约 2257 | 图片 9 | 建议阅读 5 分钟 | 评价反馈文章源自JAVA秀-https://www.javaxiu.com/3727.html

京东一面:说说@Component,@Service等注解是如何被解析的?

点击关注 ? Java面试那些事儿 文章源自JAVA秀-https://www.javaxiu.com/3727.html

点击关注下方公众号,Java面试资料 都在这里京东一面:说说@Component,@Service等注解是如何被解析的?文章源自JAVA秀-https://www.javaxiu.com/3727.html

文章源自JAVA秀-https://www.javaxiu.com/3727.html

文章源自JAVA秀-https://www.javaxiu.com/3727.html

作者:温安适文章源自JAVA秀-https://www.javaxiu.com/3727.html

来源:https://juejin.im/post/5ef5710cf265da22ce391aba文章源自JAVA秀-https://www.javaxiu.com/3727.html

京东一面:说说@Component,@Service等注解是如何被解析的?正文如下:文章源自JAVA秀-https://www.javaxiu.com/3727.html

文章源自JAVA秀-https://www.javaxiu.com/3727.html

# 前言文章源自JAVA秀-https://www.javaxiu.com/3727.html

文章源自JAVA秀-https://www.javaxiu.com/3727.html

@Component和@Service都是工作中常用的注解,Spring如何解析?文章源自JAVA秀-https://www.javaxiu.com/3727.html

# @Component解析流程文章源自JAVA秀-https://www.javaxiu.com/3727.html

找入口文章源自JAVA秀-https://www.javaxiu.com/3727.html

文章源自JAVA秀-https://www.javaxiu.com/3727.html

Spring Framework2.0开始,引入可扩展的XML编程机制,该机制要求XML Schema命名空间需要与Handler建立映射关系。文章源自JAVA秀-https://www.javaxiu.com/3727.html

文章源自JAVA秀-https://www.javaxiu.com/3727.html

该关系配置在相对于classpath下的/META-INF/spring.handlers中。文章源自JAVA秀-https://www.javaxiu.com/3727.html

京东一面:说说@Component,@Service等注解是如何被解析的?文章源自JAVA秀-https://www.javaxiu.com/3727.html

如上图所示 ContextNamespaceHandler对应<context:...> 分析的入口。文章源自JAVA秀-https://www.javaxiu.com/3727.html

文章源自JAVA秀-https://www.javaxiu.com/3727.html

找核心方法文章源自JAVA秀-https://www.javaxiu.com/3727.html

文章源自JAVA秀-https://www.javaxiu.com/3727.html

浏览ContextNamespaceHandler 文章源自JAVA秀-https://www.javaxiu.com/3727.html

京东一面:说说@Component,@Service等注解是如何被解析的?文章源自JAVA秀-https://www.javaxiu.com/3727.html

在parse中有一个很重要的注释文章源自JAVA秀-https://www.javaxiu.com/3727.html

// Actually scan for bean definitions and register them.ClassPathBeanDefinitionScanner scanner  = configureScanner(parserContext, element);文章源自JAVA秀-https://www.javaxiu.com/3727.html

大意是:ClassPathBeanDefinitionScanner#doScan是扫描BeanDefinition并注册的实现 。ClassPathBeanDefinitionScanner 的源码如下:文章源自JAVA秀-https://www.javaxiu.com/3727.html

    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 读资源装换为BeanDefinition Set<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;}
    文章源自JAVA秀-https://www.javaxiu.com/3727.html

    上边的代码,从方法名,猜测:文章源自JAVA秀-https://www.javaxiu.com/3727.html

    findCandidateComponents:从classPath扫描组件,并转换为备选BeanDefinition,也就是要做的解析@Component的核心方法。文章源自JAVA秀-https://www.javaxiu.com/3727.html

    文章源自JAVA秀-https://www.javaxiu.com/3727.html

    概要分析文章源自JAVA秀-https://www.javaxiu.com/3727.html

    文章源自JAVA秀-https://www.javaxiu.com/3727.html

    findCandidateComponents在其父类ClassPathScanningCandidateComponentProvider 中。文章源自JAVA秀-https://www.javaxiu.com/3727.html

      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;}}
      文章源自JAVA秀-https://www.javaxiu.com/3727.html

      文章源自JAVA秀-https://www.javaxiu.com/3727.html

      findCandidateComponents大体思路如下:文章源自JAVA秀-https://www.javaxiu.com/3727.html

      1.String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX resolveBasePackage(basePackage) + '/' + this.resourcePattern;                      将package转化为ClassLoader类资源搜索路径packageSearchPath,例如:com.wl.spring.boot转化为classpath*:com/wl/spring/boot/**/*.class文章源自JAVA秀-https://www.javaxiu.com/3727.html

      文章源自JAVA秀-https://www.javaxiu.com/3727.html

      2.Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);  加载搜素路径下的资源。文章源自JAVA秀-https://www.javaxiu.com/3727.html

      文章源自JAVA秀-https://www.javaxiu.com/3727.html

      3.isCandidateComponent 判断是否是备选组件文章源自JAVA秀-https://www.javaxiu.com/3727.html

      文章源自JAVA秀-https://www.javaxiu.com/3727.html

      4.candidates.add(sbd); 添加到返回结果的list文章源自JAVA秀-https://www.javaxiu.com/3727.html

      文章源自JAVA秀-https://www.javaxiu.com/3727.html

      ClassPathScanningCandidateComponentProvider#isCandidateComponent其源码如下:文章源自JAVA秀-https://www.javaxiu.com/3727.html

        protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { //省略部分代码 for (TypeFilter tf : this.includeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { return isConditionMatch(metadataReader); } } return false;}
        文章源自JAVA秀-https://www.javaxiu.com/3727.html

        includeFilters由registerDefaultFilters()设置初始值,有@Component,没有@Service啊?文章源自JAVA秀-https://www.javaxiu.com/3727.html

          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. }}
          文章源自JAVA秀-https://www.javaxiu.com/3727.html

          Spring如何处理@Service的注解的呢????文章源自JAVA秀-https://www.javaxiu.com/3727.html

          文章源自JAVA秀-https://www.javaxiu.com/3727.html

          # 查文档找思路文章源自JAVA秀-https://www.javaxiu.com/3727.html

          文章源自JAVA秀-https://www.javaxiu.com/3727.html

          查阅官方文档,下面这话:文章源自JAVA秀-https://www.javaxiu.com/3727.html

          https://docs.spring.io/spring/docs/5.0.17.RELEASE/spring-framework-reference/core.html#beans-meta-annotations文章源自JAVA秀-https://www.javaxiu.com/3727.html

          文章源自JAVA秀-https://www.javaxiu.com/3727.html

          @Component is a generic stereotype for any Spring-managed component. @Repository, @Service, and @Controller are specializations of @Component文章源自JAVA秀-https://www.javaxiu.com/3727.html

          大意如下:文章源自JAVA秀-https://www.javaxiu.com/3727.html

          文章源自JAVA秀-https://www.javaxiu.com/3727.html

          @Component是任何Spring管理的组件的通用原型。@Repository、@Service和@Controller是派生自@Component。文章源自JAVA秀-https://www.javaxiu.com/3727.html

            @Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented// @Service 派生自@Component@Componentpublic @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 "";}
            文章源自JAVA秀-https://www.javaxiu.com/3727.html

            @Component是@Service的元注解,Spring 大概率,在读取@Service,也读取了它的元注解,并将@Service作为@Component处理。文章源自JAVA秀-https://www.javaxiu.com/3727.html

            文章源自JAVA秀-https://www.javaxiu.com/3727.html

            # 探寻@Component派生性流程文章源自JAVA秀-https://www.javaxiu.com/3727.html

            文章源自JAVA秀-https://www.javaxiu.com/3727.html

            回顾ClassPathScanningCandidateComponentProvider 中的关键的代码片段如下:文章源自JAVA秀-https://www.javaxiu.com/3727.html

              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;}
              文章源自JAVA秀-https://www.javaxiu.com/3727.html

              1. 确定metadataReader文章源自JAVA秀-https://www.javaxiu.com/3727.html

              文章源自JAVA秀-https://www.javaxiu.com/3727.html

              CachingMetadataReaderFactory继承自 SimpleMetadataReaderFactory,就是对SimpleMetadataReaderFactory加了一层缓存。文章源自JAVA秀-https://www.javaxiu.com/3727.html

              其内部的SimpleMetadataReaderFactory#getMetadataReader 为:文章源自JAVA秀-https://www.javaxiu.com/3727.html

                public class SimpleMetadataReaderFactory implements MetadataReaderFactory { @Overridepublic MetadataReader getMetadataReader(Resource resource) throws IOException { return new SimpleMetadataReader(resource, this.resourceLoader.getClassLoader());} }
                文章源自JAVA秀-https://www.javaxiu.com/3727.html

                这里可以看出文章源自JAVA秀-https://www.javaxiu.com/3727.html

                MetadataReader metadataReader =new SimpleMetadataReader(...);文章源自JAVA秀-https://www.javaxiu.com/3727.html

                文章源自JAVA秀-https://www.javaxiu.com/3727.html

                2.查看match方法找重点方法文章源自JAVA秀-https://www.javaxiu.com/3727.html

                文章源自JAVA秀-https://www.javaxiu.com/3727.html

                京东一面:说说@Component,@Service等注解是如何被解析的?文章源自JAVA秀-https://www.javaxiu.com/3727.html

                AnnotationTypeFilter#matchself方法如下:文章源自JAVA秀-https://www.javaxiu.com/3727.html

                  @Overrideprotected boolean matchSelf(MetadataReader metadataReader) { AnnotationMetadata metadata = metadataReader.getAnnotationMetadata(); return metadata.hasAnnotation(this.annotationType.getName()) || (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));}
                  文章源自JAVA秀-https://www.javaxiu.com/3727.html

                  是metadata.hasMetaAnnotation法,从名称看是处理元注解,我们重点关注文章源自JAVA秀-https://www.javaxiu.com/3727.html

                  文章源自JAVA秀-https://www.javaxiu.com/3727.html

                  逐步分析文章源自JAVA秀-https://www.javaxiu.com/3727.html

                  找metadata.hasMetaAnnotation文章源自JAVA秀-https://www.javaxiu.com/3727.html

                  文章源自JAVA秀-https://www.javaxiu.com/3727.html

                  metadata=metadataReader.getAnnotationMetadata();文章源自JAVA秀-https://www.javaxiu.com/3727.html

                  metadataReader =new SimpleMetadataReader(...)文章源自JAVA秀-https://www.javaxiu.com/3727.html

                  metadata= new SimpleMetadataReader#getAnnotationMetadata()文章源自JAVA秀-https://www.javaxiu.com/3727.html

                    //SimpleMetadataReader 的构造方法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;}
                    文章源自JAVA秀-https://www.javaxiu.com/3727.html

                    文章源自JAVA秀-https://www.javaxiu.com/3727.html

                    metadata=new SimpleMetadataReader(...).getAnnotationMetadata()= new AnnotationMetadataReadingVisitor(。。)文章源自JAVA秀-https://www.javaxiu.com/3727.html

                    文章源自JAVA秀-https://www.javaxiu.com/3727.html

                    也就是说文章源自JAVA秀-https://www.javaxiu.com/3727.html

                    文章源自JAVA秀-https://www.javaxiu.com/3727.html

                    metadata.hasMetaAnnotation=AnnotationMetadataReadingVisitor#hasMetaAnnotation文章源自JAVA秀-https://www.javaxiu.com/3727.html

                    文章源自JAVA秀-https://www.javaxiu.com/3727.html

                    其方法如下:文章源自JAVA秀-https://www.javaxiu.com/3727.html

                      public class AnnotationMetadataReadingVisitor{ // 省略部分代码@Overridepublic boolean hasMetaAnnotation(String metaAnnotationType) { Collection<Set<String>> allMetaTypes = this.metaAnnotationMap.values(); for (Set<String> metaTypes : allMetaTypes) { if (metaTypes.contains(metaAnnotationType)) { return true; } } return false;}}
                      文章源自JAVA秀-https://www.javaxiu.com/3727.html

                      逻辑很简单,就是判断该注解的元注解在,在不在metaAnnotationMap中,如果在就返回true。文章源自JAVA秀-https://www.javaxiu.com/3727.html

                      文章源自JAVA秀-https://www.javaxiu.com/3727.html

                      这里面核心就是metaAnnotationMap,搜索AnnotationMetadataReadingVisitor类,没有发现赋值的地方??!。文章源自JAVA秀-https://www.javaxiu.com/3727.html

                      文章源自JAVA秀-https://www.javaxiu.com/3727.html

                      查找metaAnnotationMap赋值文章源自JAVA秀-https://www.javaxiu.com/3727.html

                      文章源自JAVA秀-https://www.javaxiu.com/3727.html

                      回到SimpleMetadataReader 的方法,文章源自JAVA秀-https://www.javaxiu.com/3727.html

                        //这个accept方法,很可疑,在赋值之前执行SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException {//省略其他代码AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader);classReader.accept(visitor, ClassReader.SKIP_DEBUG); this.annotationMetadata = visitor; }
                        文章源自JAVA秀-https://www.javaxiu.com/3727.html

                        发现一个可疑的语句:classReader.accept。文章源自JAVA秀-https://www.javaxiu.com/3727.html

                        查看accept方法文章源自JAVA秀-https://www.javaxiu.com/3727.html

                          public class ClassReader { //省略其他代码public void accept(..省略代码){ //省略其他代码 readElementValues( classVisitor.visitAnnotation(annotationDescriptor, /* visible = */ true), currentAnnotationOffset, true, charBuffer);}}
                          文章源自JAVA秀-https://www.javaxiu.com/3727.html

                          文章源自JAVA秀-https://www.javaxiu.com/3727.html

                          查看readElementValues方法文章源自JAVA秀-https://www.javaxiu.com/3727.html

                            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;}}
                            文章源自JAVA秀-https://www.javaxiu.com/3727.html

                            这里面的核心就是  annotationVisitor.visitEnd();文章源自JAVA秀-https://www.javaxiu.com/3727.html

                            确定annotationVisitor文章源自JAVA秀-https://www.javaxiu.com/3727.html

                            文章源自JAVA秀-https://www.javaxiu.com/3727.html

                            这里annotationVisitor=AnnotationMetadataReadingVisitor#visitAnnotation源码如下,注意这里传递了metaAnnotationMap!!文章源自JAVA秀-https://www.javaxiu.com/3727.html

                              public class AnnotationMetadataReadingVisitor{@Overridepublic 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);}}
                              文章源自JAVA秀-https://www.javaxiu.com/3727.html

                              annotationVisitor=AnnotationAttributesReadingVisitor文章源自JAVA秀-https://www.javaxiu.com/3727.html

                              文章源自JAVA秀-https://www.javaxiu.com/3727.html

                              查阅annotationVisitor.visitEnd()文章源自JAVA秀-https://www.javaxiu.com/3727.html

                              文章源自JAVA秀-https://www.javaxiu.com/3727.html

                              annotationVisitor=AnnotationAttributesReadingVisitor#visitEnd()文章源自JAVA秀-https://www.javaxiu.com/3727.html

                                publicclassAnnotationAttributesReadingVisitor{@Overridepublic 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); } } } }}}
                                文章源自JAVA秀-https://www.javaxiu.com/3727.html

                                内部方法recursivelyCollectMetaAnnotations 递归的读取注解,与注解的元注解(读@Service,再读元注解@Component),并设置到metaAnnotationMap,也就是AnnotationMetadataReadingVisitor 中的metaAnnotationMap中。文章源自JAVA秀-https://www.javaxiu.com/3727.html

                                文章源自JAVA秀-https://www.javaxiu.com/3727.html

                                # 总结文章源自JAVA秀-https://www.javaxiu.com/3727.html

                                文章源自JAVA秀-https://www.javaxiu.com/3727.html

                                大致如下:文章源自JAVA秀-https://www.javaxiu.com/3727.html

                                ClassPathScanningCandidateComponentProvider#findCandidateComponents文章源自JAVA秀-https://www.javaxiu.com/3727.html

                                文章源自JAVA秀-https://www.javaxiu.com/3727.html

                                1.  将package转化为ClassLoader类资源搜索路径packageSearchPath文章源自JAVA秀-https://www.javaxiu.com/3727.html

                                2.  加载搜素路径下的资源。文章源自JAVA秀-https://www.javaxiu.com/3727.html

                                3.  isCandidateComponent 判断是否是备选组件。文章源自JAVA秀-https://www.javaxiu.com/3727.html

                                文章源自JAVA秀-https://www.javaxiu.com/3727.html

                                内部调用的TypeFilter的match方法:文章源自JAVA秀-https://www.javaxiu.com/3727.html

                                AnnotationTypeFilter#matchself中metadata.hasMetaAnnotation处理元注解    metadata.hasMetaAnnotation=AnnotationMetadataReadingVisitor#hasMetaAnnotation就是判断当前注解的元注解在不在metaAnnotationMap中。文章源自JAVA秀-https://www.javaxiu.com/3727.html

                                文章源自JAVA秀-https://www.javaxiu.com/3727.html

                                AnnotationAttributesReadingVisitor#visitEnd()内部方法recursivelyCollectMetaAnnotations 递归的读取注解,与注解的元注解(读@Service,再读元注解@Component),并设置到metaAnnotationMap文章源自JAVA秀-https://www.javaxiu.com/3727.html

                                文章源自JAVA秀-https://www.javaxiu.com/3727.html

                                4. 添加到返回结果的list文章源自JAVA秀-https://www.javaxiu.com/3727.html

                                热门推荐:@所有人 你的手机号码有风险!TIOBE 公布 3 月榜单,Java 还是第一吗?唯品会一面:Spring 为何需要三级缓存解决循环依赖,而不是二级缓存?
                                文章源自JAVA秀-https://www.javaxiu.com/3727.html
                                继续阅读
                                速蛙云 - 极致体验,强烈推荐!!!购买套餐就免费送各大视频网站会员!快速稳定、独家福利社、流媒体稳定解锁!速度快,全球上网、视频、游戏加速、独立IP均支持!基础套餐性价比很高!这里不多说,我一直正在使用,推荐购买:https://www.javaxiu.com/59919.html
                                weinxin
                                资源分享QQ群
                                本站是JAVA秀团队的技术分享社区, 会经常分享资源和教程; 分享的时代, 请别再沉默!
                                沙海
                                匿名

                                发表评论

                                匿名网友 填写信息

                                :?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

                                确定