Component注解的派生性原理

it2022-05-05  93

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类配合includeFilters和excludeFilters定制化批量注册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实现上看,ClassMetadataReadingVisitor和AnnotationMetadataReadingVisitor分别是ClassMetadatta和AnnotationMetadata实现类。

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

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

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。因此,元注解的实现集中到AnnotationMetadataReadingVisitor和AnnotationAttributesReadingVisitor之中。

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

 

 

转载于:https://www.cnblogs.com/liuenyuan1996/p/11134827.html


最新回复(0)