Component注解的派生性原理

1:模式注解

Stereotype Annotation俗称为模式注解。Spring核心部分提供了几种内建的模式注解,如@Component,@Repository,@Service,@Controller,@Configuration等。这些注解均派生于@Component

由于Java语言规定,Annotation不允许继承,没有类派生子类的特性,因此Spring采用元标注的方式实现注解之间的派生

2:@Component派生性

@Component注解作为Spring容器托管的通用模式组件,任何被@Component标注的组件均为组件扫描的候选对象。

任何论证过程离不开所处的环境,需要开发人员具备一定工程意识,包括软件版本,特性范围,兼容情况等。因此,论证过程从最低版本开始推导,逐步证明不同版本得提升和差异。

3:@Component注解派生性原理

ClassPathBeanDefinitionScanner#doScan(String... basePackages)调用时,它利用basePackages参数迭代执行的findCandidateComponents(String basePackage),每次执行结果都生成候选的BeanDefinition集合,即candidates变量。


public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider{
    ...
 protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Assert.notEmpty(basePackages, "At least one base package must be specified");
        //获取候选的BeanDefinition集合
        Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
        for (String basePackage : basePackages) {
            Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
            ...
        }
        return beanDefinitions;
    }   
    ...
}

而findCandidateComponents(String basePackage)从父类ClassPathScanningCandidateComponentProvider

中继承。


public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
    ...
 public Set<BeanDefinition> findCandidateComponents(String basePackage) {
        Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
        try {
            //获取查询的package,并处理占位符情况${...},转换为ClassLoader资源(.class)搜索路径
            String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                    resolveBasePackage(basePackage) + '/' + this.resourcePattern;
            Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
            ...
            //resource迭代执行,当资源可读取时,获取该资源的MetadataReader对象
            for (Resource resource : resources) {
                ...
                if (resource.isReadable()) {
                    try {
                        //包含了类和注解元信息读取方法
                        MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
                        //判断资源是否为候选的组件,通过excludeFilters和includeFilters进行判断
                        if (isCandidateComponent(metadataReader)) {
                            //基于ASM,支持AnnotatedBeanDefinition接口
                            ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                            sbd.setResource(resource);
                            sbd.setSource(resource);
                            //判断BeanDefinition是否候选组件
                            if (isCandidateComponent(sbd)) {
                                ...
                                candidates.add(sbd);
                            }
                            ...
            }
        }
        ...
        return candidates;
    }   
    ...
        
    
    /**
     * Determine whether the given class does not match any exclude filter
     * and does match at least one include filter.
     * @param metadataReader the ASM ClassReader for the class
     * @return whether the class qualifies as a candidate component
     */
    protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException{
        for (TypeFilter tf : this.excludeFilters) {
            if (tf.match(metadataReader, this.metadataReaderFactory)) {
                return false;
            }
        }
        for (TypeFilter tf : this.includeFilters) {
            if (tf.match(metadataReader, this.metadataReaderFactory)) {
                return isConditionMatch(metadataReader);
            }
        }
        return false;
    }
                
                
        /**
     * Determine whether the given bean definition qualifies as candidate.
     * <p>The default implementation checks whether the class is not an interface
     * and not dependent on an enclosing class.
     * <p>Can be overridden in subclasses.
     * @param beanDefinition the bean definition to check
     * @return whether the bean definition qualifies as a candidate component
     */
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        AnnotationMetadata metadata = beanDefinition.getMetadata();
        return (metadata.isIndependent() && (metadata.isConcrete() ||
                (metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
    }
                
                
                
    /**
     * Register the default filter for {@link Component @Component}.
     * <p>This will implicitly register all annotations that have the
     * {@link Component @Component} meta-annotation including the
     * {@link Repository @Repository}, {@link Service @Service}, and
     * {@link Controller @Controller} stereotype annotations.
     * <p>Also supports Java EE 6's {@link javax.annotation.ManagedBean} and
     * JSR-330's {@link javax.inject.Named} annotations, if available.
     *
     */
    @SuppressWarnings("unchecked")
    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));
            ...
        }
        try {
            this.includeFilters.add(new AnnotationTypeFilter(
    ((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
    ...
    }
    }
}

默认情况下,ClassPathScanningCandidateComponentProvider构造参数useDefaultFilters为true,并且显示传递给父类构造参数。该方法给属性includeFilters增添了@Component类型AnnotationTypeFilter的TypeFilter。

ClassPathBeanDefinitionScanner默认过滤器引入标注@Component,@Repository,@Service或者@Controller等类。同理,它也能够标注所有@Component的"派生"注解。

@Component注解只包含一个value属性定义,所以其“派生”的注解也只能包含一个vlaue属性定义。

Dubbo实现@Service注解扫描实例:

ClassPathBeanDefinitionScanner允许自定义类型过滤规则。因此,Dubbo的@Service没有标注@Component情况下,通过scanner.addIncludeFilter(new AnnotationTypeFilter(Service.class))方式达到识别@Service标注类情况。但是没有使用@Component注解的派生性。

Mybatis实现@Mapper注解扫描实例:

public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner{
    
    ...
        
  public ClassPathMapperScanner(BeanDefinitionRegistry registry) {
    super(registry, false);
  }
    
   /**
   * Configures parent scanner to search for the right interfaces. It can search
   * for all interfaces or just for those that extends a markerInterface or/and
   * those annotated with the annotationClass
   */
  public void registerFilters() {
    boolean acceptAllInterfaces = true;
​
    // if specified, use the given annotation and / or marker interface
    if (this.annotationClass != null) {
      addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
      acceptAllInterfaces = false;
    }
​
    // override AssignableTypeFilter to ignore matches on the actual marker interface
    if (this.markerInterface != null) {
      addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
        @Override
        protected boolean matchClassName(String className) {
          return false;
        }
      });
      acceptAllInterfaces = false;
    }
​
    if (acceptAllInterfaces) {
      // default include filter that accepts all classes
      addIncludeFilter(new TypeFilter() {
        @Override
        public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
          return true;
        }
      });
    }
​
    // exclude package-info.java
    addExcludeFilter(new TypeFilter() {
      @Override
      public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        String className = metadataReader.getClassMetadata().getClassName();
        return className.endsWith("package-info");
      }
    });
  }
​
  /**
   * {@inheritDoc}
   */
  @Override
  protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
    return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
  }
    
    
  private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (GenericBeanDefinition) holder.getBeanDefinition();
     
       ...
​
      //复杂对象构建考虑使用FactoryBean接口           
      // the mapper interface is the original class of the bean
      // but, the actual class of the bean is MapperFactoryBean
     //添加泛型参数         
           definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
     // issue #59
      definition.setBeanClass(this.mapperFactoryBean.getClass());
​
      definition.getPropertyValues().add("addToConfig", this.addToConfig);
      ...
    }
  }
    
  ...
}

4:思考扩展

思考1:利用ClassPathBeanDefinitionScanner类配合includeFiltersexcludeFilters定制化批量注册Bean到Spring容器中。常常可以通过注解方式来包含或者排除候选类。

TypeFilter常用实现

  • AnnotationTypeFilter:注解类型过滤器

  • AssignableTypeFilter:确定此对象表示的类或者接口是否为给定类或者接口相同。

  • RegexPatternTypeFilter:判断给定的类名是否符合指定正则表达式。

思考2:复杂对象构建考虑使用FactoryBean实现类。

思考3:如果是读取类和注解信息可以考虑基于ASM或者反射,使用方式往下可以获取。当获取已加载的类信息可以考虑反射(反射大前提是被反射的Class被ClassLoader加载),ASM用于不需要将类路径package下的Class全部加载,Spring应用指定Java package扫描Spring模式注解时,利用的就是基于ASM方式获取类或者注解信息。基于ASM获取会获得更大性能。

思考4:资源读取考虑使用ResourcePatternResolver,这个对象的获取可以通过Spring提供的工具类

ResourcePatternUtils.getResourcePatternResolver(resourceLoader)。在使用的时候,考虑处理

占位符${...}的情况,注意资源是否可读。

5:多层次@Component派生性

(1):具体发展过程不再细说,详解请看SpringBoot编程思想这本书。其多层次@Component注解派生性构建在Spring4.x。其核心处理类为AnnotationMetadataReadingVisitor,其采用递归的方式查找元注解

(2):Spring中,MetadataReader接口唯一实现非公开类SimpleMetadataReader。可以通过SimpleMetadataReaderFactory(ASM字节码操作)CachingMetadataReaderFactory获取。

其中在SimpleMetadataReader实现上看,ClassMetadataReadingVisitorAnnotationMetadataReadingVisitor分别是ClassMetadattaAnnotationMetadata实现类。

由于ClassPathBeanDefinitionScanner在寻找候选的BeanDefinition过程中,将指定basePackage参数下

的*.class资源进行元信息解析,也就是ClassMetadataAnnotationMetadata对象。

AnnotationMetadataReadingVisitor实现上使用了AnnotationAttributesReadingVisitor,该类主要实现方法是visitEnd()Spring2.5实现未采用层次递归获取Annotation[],所以仅支持单层次的@Component派生。Spring3.x实现仅两层@Component派生。Spring4.x开始采用递归方式查找元注解。相关实现在Spring为公开类AnnotationAttributesReadingVisitor。

final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttributesVisitor {

    private final MultiValueMap<String, AnnotationAttributes> attributesMap;

    private final Map<String, Set<String>> metaAnnotationMap;


    ...


    @Override
    public void visitEnd() {
        super.visitEnd();

        Class<?> 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);
            }
            Set<Annotation> visited = new LinkedHashSet<Annotation>();
            Annotation[] metaAnnotations = AnnotationUtils.getAnnotations(annotationClass);
            if (!ObjectUtils.isEmpty(metaAnnotations)) {
                for (Annotation metaAnnotation : metaAnnotations) {
                    if (!AnnotationUtils.isInJavaLangAnnotationPackage(metaAnnotation)) {
                        recursivelyCollectMetaAnnotations(visited, metaAnnotation);
                    }
                }
            }
            if (this.metaAnnotationMap != null) {
                Set<String> metaAnnotationTypeNames = new LinkedHashSet<String>(visited.size());
                for (Annotation ann : visited) {
                    metaAnnotationTypeNames.add(ann.annotationType().getName());
                }
                this.metaAnnotationMap.put(annotationClass.getName(), metaAnnotationTypeNames);
            }
        }
    }

    private void recursivelyCollectMetaAnnotations(Set<Annotation> visited, Annotation annotation) {
        Class<? extends Annotation> annotationType = annotation.annotationType();
        String annotationName = annotationType.getName();
        if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotationName) && visited.add(annotation)) {
            try {
                // Only do attribute scanning for public annotations; we'd run into
                // IllegalAccessExceptions otherwise, and we don't want to mess with
                // accessibility in a SecurityManager environment.
                if (Modifier.isPublic(annotationType.getModifiers())) {
                    this.attributesMap.add(annotationName,
                            AnnotationUtils.getAnnotationAttributes(annotation, false, true));
                }
          //实现递归查找元注解
for (Annotation metaMetaAnnotation : annotationType.getAnnotations()) { recursivelyCollectMetaAnnotations(visited, metaMetaAnnotation); } } catch (Throwable ex) { if (logger.isDebugEnabled()) { logger.debug("Failed to introspect meta-annotations on [" + annotation + "]: " + ex); } } } } }

(3):思考扩展

  • 考虑使用ASM的方式读取类或者注解相关信息。(不需要全部将指定路径下的类加载)

    • MetadataReaderFactory:获取MetadataReader工厂

      • SimpleMetadataReaderFactory:简单获取MetadataReader工厂实现

        • ClassReader:基于ASM读取类相关信息,公开类,不建议单独使用。

        • AnnotationMetadataReadingVisitor:基于ASM读取注解元数据相关信息,不建议单独使用。

        • MethodMetadataReadingVisitor:基于ASM读取方法相关信息,不建议单独使用。

      • CachingMetadataReaderFactory:继承SimpleMetadataReaderFactory,增加缓存MetadataReader资源功能。

    • MetadataReader:获取访问类和注解相关信息。通过MetadataReaderFactory获取。

      • Resource getResource():获取类文件资源引用

      • ClassMetadata getClassMetadata():读取基础类的基本元数据

      • AnnotationMetadata getAnnotationMetadata():读取底层类完整注解元数据,包含注解方法的注解元数据。

  • 考虑使用反射的方式读取类或者注解相关信息(比较费时而且该类必须被ClassLoader加载)

    • StandardClassMetadata:基于反射读取类元数据,可建议单独使用。

    • StandardAnnotationMetadata:基于反射读取注解元数据,可建议单独使用

    • StandardMethodMetadata:基于反射读取方法元数据,可建议单独使用

  • 考虑使用Spring内部支持的有用工具类,都是来自于spring-core包中。多使用spring内建API,学习他们的长处。

    • ClassUtils:类工具类

    • CollectionUtils:集合工具类

    • NumberUtils:Number工具类

    • MimeTypeUtils:媒体类型工具类

    • IdGenerator:Id生成器

    • StringUtils:字符串工具类

    • ResourceUtils:资源工具类

    • ReflectionUtils:反射工具类

    • MethodIntrospector:方法自省工具类(EventListenerMethodProcessor#processBean中有使用)

    • PatternMatchUtils:正则资源匹配工具类

    • ObjectUtils:对象工具类

3:组合注解

组合注解指某个注解"元标注"一个或多个其他注解,其目的在于将这些关联的注解行为组合成单个自定义注解。

Spring Framework的类加载通过ASM实现,如ClassReader。相对于ClassLoader体系,Spring ASM更为底层,读取的是类资源,直接操作其中的字节码,获取相关元信息。如MetadataReader接口

/**
 * Simple facade for accessing class metadata,
 * as read by an ASM {@link org.springframework.asm.ClassReader}.
 *
 * @author Juergen Hoeller
 * @since 2.5
 */
public interface MetadataReader {
​
    /**
     * Return the resource reference for the class file.
     */
    Resource getResource();
​
    /**
     * Read basic class metadata for the underlying class.
     */
    ClassMetadata getClassMetadata();
​
    /**
     * Read full annotation metadata for the underlying class,
     * including metadata for annotated methods.
     */
    AnnotationMetadata getAnnotationMetadata();
​
}

AnnotationMetadataReadingVisitor同时实现了ClassMetadata及AnnotationMetadata。因此,元注解的实现集中到AnnotationMetadataReadingVisitorAnnotationAttributesReadingVisitor之中。

MetadataReader对象通过MetadataReaderFactory对象获取。


/**
 * Factory interface for {@link MetadataReader} instances.
 * Allows for caching a MetadataReader per original resource.
 *
 * @author Juergen Hoeller
 * @since 2.5
 * @see SimpleMetadataReaderFactory
 * @see CachingMetadataReaderFactory
 */
public interface MetadataReaderFactory {
​
    /**
     * Obtain a MetadataReader for the given class name.
     * @param className the class name (to be resolved to a ".class" file)
     * @return a holder for the ClassReader instance (never {@code null})
     * @throws IOException in case of I/O failure
     */
    MetadataReader getMetadataReader(String className) throws IOException;
​
    /**
     * Obtain a MetadataReader for the given resource.
     * @param resource the resource (pointing to a ".class" file)
     * @return a holder for the ClassReader instance (never {@code null})
     * @throws IOException in case of I/O failure
     */
    MetadataReader getMetadataReader(Resource resource) throws IOException;
​
}
​
 

具体某个注解的元注解信息则通过getMetaAnnotationTypes(String)方法查询。

AnnotationMetadata实现AnnotationMetadataReadingVisitor(ASM实现)StandardAnnotationMetadata(反射)

  • 注解元信息抽象:AnnotationMetadata

    • AnnotationMetadataReadingVisitor

      • AnnotationAttributesReadingVisitor(递归查找元注解)

  • 类元信息抽象:ClassMetadata

  • 方法元信息抽象:MethodMetadata

  • 注解属性抽象:AnnotationAttributes

  • 属性环境抽象:Environment

  • 属性文件抽象:PropertySource

  • 元信息读取抽象:MetadataReader

    • 通过MetadataReaderFactory获取

方法内省:MethodIntrospector

        Map<Method, EventListener> annotatedMethods = null;
            annotatedMethods = MethodIntrospector.selectMethods(targetType,
                        (MethodIntrospector.MetadataLookup<EventListener>) method ->
                                AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));

 

 

posted @ 2019-07-04 20:49  海渊  阅读(4020)  评论(0编辑  收藏  举报