• 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));

     

  • 相关阅读:
    [LeetCode] Best Time to Buy and Sell Stock with Transaction Fee 买股票的最佳时间含交易费
    Visual Studio Many Projects in One Solution VS中多工程开发
    [LeetCode] 713. Subarray Product Less Than K 子数组乘积小于K
    [LeetCode] Minimum ASCII Delete Sum for Two Strings 两个字符串的最小ASCII删除和
    [LeetCode] Erect the Fence 竖立栅栏
    3D Slicer Reconstruct CT/MRI
    [LeetCode] Partition to K Equal Sum Subsets 分割K个等和的子集
    [LeetCode] Degree of an Array 数组的度
    [LeetCode] Count Binary Substrings 统计二进制子字符串
    [LeetCode] Max Area of Island 岛的最大面积
  • 原文地址:https://www.cnblogs.com/liuenyuan1996/p/11134827.html
Copyright © 2020-2023  润新知