Component註解的派生性原理

1:模式註解

Stereotype Annotation俗稱爲模式註解。Spring核心部分提供了幾種內建的模式註解,如@Component,@Repository,@Service,@Controller,@Configuration等。這些註解均派生於@Componentjava

因爲Java語言規定,Annotation不容許繼承,沒有類派生子類的特性,所以Spring採用元標註的方式實現註解之間的派生正則表達式

2:@Component派生性

@Component註解做爲Spring容器託管的通用模式組件,任何被@Component標註的組件均爲組件掃描的候選對象。spring

任何論證過程離不開所處的環境,須要開發人員具有必定工程意識,包括軟件版本,特性範圍,兼容狀況等。所以,論證過程從最低版本開始推導,逐步證實不一樣版本得提高和差別。編程

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)從父類ClassPathScanningCandidateComponentProviderapp

中繼承。ide

 
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開始採用遞歸方式查找元註解。

(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));
相關文章
相關標籤/搜索