关于java:百度二面ComponentService等注解是如何被解析的

37次阅读

共计 12104 个字符,预计需要花费 31 分钟才能阅读完成。

前言

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

一、@Component 解析流程

找入口

Spring Framework2.0 开始,引入可扩大的 XML 编程机制,该机制要求 XML Schema 命名空间须要与 Handler 建设映射关系。

该关系配置在绝对于 classpath 下的 /META-INF/spring.handlers 中。

如上图所示 ContextNamespaceHandler 对应 context:… 剖析的入口。

找外围办法

浏览 ContextNamespaceHandler

在 parse 中有一个很重要的正文

// Actually scan for bean definitions and register them.

ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);

粗心是:ClassPathBeanDefinitionScanner#doScan 是扫描 BeanDefinition 并注册的实现。

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 读资源装换为 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;
}

上边的代码,从办法名,猜想:

findCandidateComponents:从 classPath 扫描组件,并转换为备选 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 转化为 ClassLoader 类资源搜寻门路 packageSearchPath,例如:com.wl.spring.boot 转化为 classpath*:com/wl/spring/boot/**/*.class
  • Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); 加载搜素门路下的资源。
  • isCandidateComponent 判断是否是备选组件
  • candidates.add(sbd); 增加到返回后果的 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() 设置初始值,有 @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 的注解的呢????

二、查文档找思路

查阅官网文档,上面这话:

https://docs.spring.io/spring…

@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 解决。

三、探寻 @Component 派生性流程

回顾 ClassPathScanningCandidateComponentProvider 中的要害的代码片段如下:

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,就是对 SimpleMetadataReaderFactory 加了一层缓存。

其外部的 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 办法找重点办法

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 法,从名称看是解决元注解,咱们重点关注

逐渐剖析

找 metadata.hasMetaAnnotation

metadata=metadataReader.getAnnotationMetadata();

metadataReader =new SimpleMetadataReader(...)

metadata= new SimpleMetadataReader#getAnnotationMetadata()

//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;
}

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;
}
}

逻辑很简略,就是判断该注解的元注解在,在不在 metaAnnotationMap 中,如果在就返回 true。

这外面外围就是 metaAnnotationMap,搜寻 AnnotationMetadataReadingVisitor 类,没有发现赋值的中央??!。

查找 metaAnnotationMap 赋值

回到 SimpleMetadataReader 的办法,

// 这个 accept 办法,很可疑,在赋值之前执行
SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException {
// 省略其余代码
AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader);
classReader.accept(visitor, ClassReader.SKIP_DEBUG);
 this.annotationMetadata = visitor;
 }

发现一个可疑的语句: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);
            }
         }
      }
   }
}
}

外部办法 recursivelyCollectMetaAnnotations 递归的读取注解,与注解的元注解(读 @Service,再读元注解 @Component),并设置到 metaAnnotationMap,也就是 AnnotationMetadataReadingVisitor 中的 metaAnnotationMap 中。

总结

大抵如下:

ClassPathScanningCandidateComponentProvider#findCandidateComponents

1. 将 package 转化为 ClassLoader 类资源搜寻门路 packageSearchPath

2. 加载搜素门路下的资源。

3.isCandidateComponent 判断是否是备选组件。

外部调用的 TypeFilter 的 match 办法:

  • AnnotationTypeFilter#matchself 中 metadata.hasMetaAnnotation 解决元注解
  • metadata.hasMetaAnnotation=AnnotationMetadataReadingVisitor#hasMetaAnnotation

就是判断以后注解的元注解在不在 metaAnnotationMap 中。

AnnotationAttributesReadingVisitor#visitEnd() 外部办法 recursivelyCollectMetaAnnotations 递归的读取注解,与注解的元注解(读 @Service,再读元注解 @Component),并设置到 metaAnnotationMap

4. 增加到返回后果的 list

写在最初

欢送大家关注我的公众号【 惊涛骇浪如码 】,海量 Java 相干文章,学习材料都会在外面更新,整顿的材料也会放在外面。

感觉写的还不错的就点个赞,加个关注呗!点关注,不迷路,继续更新!!!

正文完
 0