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