文章目录

  • 1. 引言
  • 2. Spring注解编程模型
    • 2.1 元注解(Meta-Annotations)
    • 2.2 Spring模式注解(Stereotype Annotations)
    • 2.3 Spring组合注解(Composed Annotations)
      • 2.3.1 MetadataReader
    • 2.4 Spring注解属性别名和覆盖(Attribute Aliases and Overrides)

1. 引言

模式注解使框架的配置变得简洁明了,从Spring Framework 3.1开始Spring开始全面支持面向注解配置,其中一些核心注解如下

Spring模式注解:

Spring 注解 场景说明 起始版本
@Repository 数据仓库模式注解 2.0
@Component 通用组件模式注解 2.5
@Service 服务模式注解 2.5
@Controller Web控制器模式注解 2.5
@Configuration 配置类模式注解 3.0

装配注解:

Spring 注解 场景说明 起始版本
@ImportResource 替换XML元素 2.5
@Import 导入bean或者@configuration配置类 3.0
@ComponentScan 扫描指定package下标注Spring模式注解的类 3.1

依赖注入注解如下表所示:

Spring 注解 场景说明 起始版本
@Autowired Bean依赖注入,支持多种依赖查找方式 2.5
@Qualifier 细粒度限定@autowired注入 2.5
Spring 注解 场景说明 起始版本
@Resource @Bean依赖注入,仅支持名称依赖查找方式 2.5

Bean定义注解如下表所示

Spring 注解 场景说明 起始版本
@Bean 替换XML元素 3.0
@DependsOn 替换XML元素 3.0
@Lazy 替换XML元素 3.0
@Primary 替换XML元素 3.0
@Role 替换XML元素 3.1
@Lookup 替换XML元素 4.1

Spring条件装配注解:

Spring 注解 场景说明 起始版本
@Profile 配置化条件装配 3.1
@Conditional 编程条件装配 4.0

配置属性注解:

Spring 注解 场景说明 起始版本
@PropertySource 配置属性抽象 3.1
@PropertySources @PropertySource集合注解 4.0

生命周期回调注解

Java 注解 场景说明 起始版本
@PostConstruct 替换XML元素 2.5
@PreDestroy 替换XML元素 2.5

可以标注注解的属性

Java 注解 场景说明 起始版本
@AliasFor 别名注解属性,实现复用的目的 4.2

性能注解:

Java 注解 场景说明 起始版本
@Indexed 提升Spring模式注解的扫描效率 5.0

2. Spring注解编程模型

主要有四个方面分别是:

  • 元注解(Meta-Annotations)
  • Spring模式注解(Stereotype Annotations)
  • Spring组合注解(Composed Annotations)
  • Spring注解属性别名和覆盖(Attribute Aliases and Overrides)

2.1 元注解(Meta-Annotations)

元注解指一个能声明在其他注解上的注解,如果一个注解标注在其他注解上,那么他就是元注解。根据Java语言规范,注解之间不存在继承关系,因此可以通过元注解来注解到另一个注解达到“派生”的作用,这种“派生”特性需要确保注解之间的属性方法签名完全一致.

2.2 Spring模式注解(Stereotype Annotations)

引用Spring重的Wiki描述里的一句话

A sterotype annotation is an annotation that is used to declare the role that a componnet plays within the application.

简而言之Stereotype Annotations就是说明组件扮演的角色,以@Component注解为例,@Service、@Repository、@Controler、@RestController及@Configuration都包含@Component注解,所以包含@Component的功能,同时他们在不同的场景下扮演不同的角色。在Spring Framework 4.x 之前以@Component注解为例,只能识别两层的@Component派生注解,在Spring Framework 4.x 之后可以递归识别多层次的@Component派生注解。
扫描注解并模拟初始化过程简单的原理我在另一篇Java基础 注解中有简要的分析了一下,主要过程就是根据配置的basePackage属性到该路径下搜索有相应注解标记的类。
需要另外注意的是Spring在扫描的过程中还会有excludeFilters和includeFilters,排除excludeFilters中的注解,属于includeFilters中的注解将会通过筛选条件(默认初始化时includeFilters会加入@Component)。
还有一点是,处于性能方面的Spring获取底层元数据的方式是通过ASM实现的,而不是通过反射,如ClassReader类,相对于ClassLoader体系,Spring ASM更为底层,读取的是类资源,直接操作的是其中的字节码,获取相关元信息,在读取元信息方面Spring抽象出MetadataReader接口。

2.3 Spring组合注解(Composed Annotations)

其目的在于将多个注解行为组合成单个自定义注解,比如@SpringBootApplication注解由@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan等几个注解组成,Spring并没有考虑通过Java反射的手段来解析元注解信息,而是抽象出AnnotationMetadata接口,其实现类为AnnotationMetadataReadingVisitor,从Spring 4.0开始,在初始化过程中AnnotationMetadataReadingVisitor所关联的AnnotationAttributesReadingVisitor采用递归查找元注解,并保存在AnnotationMetadataReadingVisitor的metaAnnotationMap字段中。
其中核心代码如下:

package org.springframework.core.type.classreading;/*** ASM visitor which looks for annotations defined on a class or method,* including meta-annotations.** <p>This visitor is fully recursive, taking into account any nested* annotations or nested annotation arrays.** @author Juergen Hoeller* @author Chris Beams* @author Phillip Webb* @author Sam Brannen* @since 3.0*/
final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttributesVisitor {private final MultiValueMap<String, AnnotationAttributes> attributesMap;private final Map<String, Set<String>> metaAnnotationMap;public AnnotationAttributesReadingVisitor(String annotationType,MultiValueMap<String, AnnotationAttributes> attributesMap, Map<String, Set<String>> metaAnnotationMap,@Nullable ClassLoader classLoader) {super(annotationType, new AnnotationAttributes(annotationType, classLoader), classLoader);this.attributesMap = attributesMap;this.metaAnnotationMap = metaAnnotationMap;}@Overridepublic void visitEnd() {super.visitEnd();Class<? extends Annotation> 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);}if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotationClass.getName())) {try {Annotation[] metaAnnotations = annotationClass.getAnnotations();if (!ObjectUtils.isEmpty(metaAnnotations)) {Set<Annotation> visited = new LinkedHashSet<>();for (Annotation metaAnnotation : metaAnnotations) {//递归查找元注解recursivelyCollectMetaAnnotations(visited, metaAnnotation);}if (!visited.isEmpty()) {Set<String> metaAnnotationTypeNames = new LinkedHashSet<>(visited.size());for (Annotation ann : visited) {metaAnnotationTypeNames.add(ann.annotationType().getName());}this.metaAnnotationMap.put(annotationClass.getName(), metaAnnotationTypeNames);}}}catch (Throwable ex) {if (logger.isDebugEnabled()) {logger.debug("Failed to introspect meta-annotations on " + annotationClass + ": " + ex);}}}}}//递归查找元注解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);}}}}}

2.3.1 MetadataReader

在读取元信息方面,Spring抽象出MetadataReader接口:

package org.springframework.core.type.classreading;/*** 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();}

需要注意的是无论是ClassMetadata还是AnnotationMetadata,均没有Java Class和Annotation API那样丰富的关联属性,MetadataReader有明显的资源特性,getResource()方法关联了类资源的Resource信息,他在Spring Framework中有一个final实现类SimpleMetadataReader,其关联的ClassMetadata和AnnotationMetadata信息在构造阶段完成初始化

package org.springframework.core.type.classreading;/*** {@link MetadataReader} implementation based on an ASM* {@link org.springframework.asm.ClassReader}.** <p>Package-visible in order to allow for repackaging the ASM library* without effect on users of the {@code core.type} package.** @author Juergen Hoeller* @author Costin Leau* @since 2.5*/
final class SimpleMetadataReader implements MetadataReader {private final Resource resource;private final ClassMetadata classMetadata;private final AnnotationMetadata annotationMetadata;SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException {InputStream is = new BufferedInputStream(resource.getInputStream());ClassReader classReader;try {classReader = new ClassReader(is);}catch (IllegalArgumentException ex) {throw new NestedIOException("ASM ClassReader failed to parse class file - " +"probably due to a new Java class file version that isn't supported yet: " + resource, ex);}finally {is.close();}AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader);classReader.accept(visitor, ClassReader.SKIP_DEBUG);this.annotationMetadata = visitor;// (since AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor)this.classMetadata = visitor;this.resource = resource;}@Overridepublic Resource getResource() {return this.resource;}@Overridepublic ClassMetadata getClassMetadata() {return this.classMetadata;}@Overridepublic AnnotationMetadata getAnnotationMetadata() {return this.annotationMetadata;}}

AnnotationMetadataReadingVisitor同时实现了ClassMetadata和AnnotationMetadata接口,在ClassPathScanningCandidateComponentProvider#findCandidateComponents(String)方法中MetadataReader实例由由metadataReaderFactory.getMetadateReader实例生成。

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {Set<BeanDefinition> candidates = new LinkedHashSet<>();try {String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +resolveBasePackage(basePackage) + '/' + this.resourcePattern;Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);boolean traceEnabled = logger.isTraceEnabled();boolean debugEnabled = logger.isDebugEnabled();for (Resource resource : resources) {if (traceEnabled) {logger.trace("Scanning " + resource);}if (resource.isReadable()) {try {//默认是CachingMetadataReaderFactoryMetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);if (isCandidateComponent(metadataReader)) {ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);sbd.setResource(resource);sbd.setSource(resource);if (isCandidateComponent(sbd)) {if (debugEnabled) {logger.debug("Identified candidate component class: " + resource);}candidates.add(sbd);}else {if (debugEnabled) {logger.debug("Ignored because not a concrete top-level class: " + resource);}}}else {if (traceEnabled) {logger.trace("Ignored because not matching any filter: " + resource);}}}catch (Throwable ex) {...}}else {if (traceEnabled) {logger.trace("Ignored because not readable: " + resource);}}}}catch (IOException ex) {...}return candidates;}

这里的getMetadataReaderFactory()默认是CachingMetadataReaderFactory,他提供getMetadataReader两种重载方法

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

下面我们通过一个简单的栗子来通过MetadataReader获取一下元注解

/*** 通过MetadataReaderFactory获取metadataReader实例并获取元信息*/
@RestController
public class AnnotationMetadataInfo {public static void main(String[] args) throws IOException {String className = AnnotationMetadataInfo.class.getName();MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();//通过class全限定名称获取MetadataReader实例MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);//获取当前类的注解元信息AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();//获取当前类的注解元信息的全限定名Set<String> set = annotationMetadata.getAnnotationTypes();System.out.println(set);set.forEach(annotationTypeName -> {//通过注解全限定名找到其所有的元注解annotationMetadata.getMetaAnnotationTypes(annotationTypeName).forEach(metaAnnotationTypeName -> {System.out.println(metaAnnotationTypeName);});});}
}

2.4 Spring注解属性别名和覆盖(Attribute Aliases and Overrides)

设计模式——Spring注解编程模型相关推荐

  1. Spring注解编程基石(一)

    目录 Java注解 Java原生注解 元注解 Stereotype 注解 组合注解 组合注解实现的基础 @AliasFor 隐式别名 @AliasFor 和 @Inherited 区别 注解解析工具 ...

  2. Spring注解编程基石(二)

    目录 辅助类 AttributeMethods AnnotationFilter RepeatableContainers MergedAnnotation接口 AnnotationsProcesso ...

  3. Spring注解编程基石(三)

    目录 AnnotationUtils 源码分析 方法列表 方法源码 AnnotatedElementUtils 源码分析 Spring注解编程基石(一) Spring注解编程基石(二) Spring注 ...

  4. Spring注解编程基石(四)

    目录 AnnotationsScanner 扫描方法 扫描source为Class方法 扫描source为Method方法 辅助方法 MergedAnnotationSelector MergedAn ...

  5. requestmapping注解作用_Java高级架构师-Spring 注解编程之注解属性别名与覆盖

    欢迎关注头条号:Java小野猫 注解属性方法 在进入了解 Spring 注解属性功能之前,我们先看一个正常 Java 注解. 在注解中,属性方法与其他类/接口方法写法类似,但是存在一些区别. 注解属性 ...

  6. Spring核心编程思想

    第01章:Spring Framework总览 (12讲)       01.课程介绍.mp4       02.内容综述.mp4       03.课前准备:学习三件套(工具.代码与大脑).mp4 ...

  7. [201903][Spring Boot 编程思想][核心篇][小马哥][著]

    [201903][Spring Boot 编程思想][核心篇][小马哥][著] The Java Community Process(SM) Program https://jcp.org/en/ho ...

  8. 小马哥spring编程核心思想_极客小马哥Spring核心编程思想

    42 | 依赖查找的今世前生:Spring IoC容器从Java标准中学到了什么? 43 | 单一类型依赖查找:如何查找已知名称或类型的Bean对象? 44 | 集合类型依赖查找:如何查找已知类型多个 ...

  9. spring controller 增加header字段forward_Spring 注解编程之模式注解

    上篇文章研究 Spring XML Schema 扩展进制,这段时候一直研究 Spring 注解编程的原理.原本以为有了之前研究基础,可以很快理解注解编程原理.没想到这个过程非常困难,注解编程源码难度 ...

最新文章

  1. android air创建文件夹,安卓版Airdrop将上线:无需安装APP,轻松实现文件隔空投送...
  2. VS2015 将*.xaml.cs文件包裹在*.xaml文件下
  3. python 通登录银行_Python3 适合初学者学习的银行账户登录系统实例
  4. org.springframework.web.multipart.MultipartException: The current request is not a multipart request
  5. SAP CAM - Cloud Access Manager
  6. 【渝粤教育】电大中专电大中专职业健康与安全考试考核试题作业 题库
  7. (转ORCLE导入导出命令)
  8. 怎么使用hsqldb 和 mybatis 构造单元测试
  9. 按首字母排序(汉字、英文、数字)简单实现
  10. office办公哪个版本稳定好用
  11. Java常用英语单词(笔记整理)
  12. 最小包围球(附完整代码)
  13. 渗透测试如何学习? (*╹▽╹*) 信息收集 ~ 其一
  14. 80后,我们难忘的电视剧
  15. 2021年全球手机市场最大赢家除了苹果,还有OPPO系
  16. iOS IAP应用内购详细步骤和问题总结指南
  17. java epson_无法与Epson POS打印机通信
  18. 动态函数监控技术在缓冲区溢出检测中的应用
  19. html如何实现在线客服,在线客服.html
  20. linux系统log可以删吗,Linux 系统 /var/log/journal/ 垃圾日志清理

热门文章

  1. ssm基于javaweb的医疗健康知识管理系统设计与实现 毕业设计-附源码131903
  2. CUDA编程实现求解单源Bellman-Ford最短 路径算法
  3. 《Modelica教程》by Fritzson 导言部分
  4. SQL Server 常用查询练习
  5. 7-3 小孩子才做选择,大人全都要 (10 分)
  6. 祝福大家新年快乐,身体健康,工作顺利,万事如意!
  7. linux 网络错误代码,Linux版本登录提示网络错误
  8. 计算机硬盘只显示c盘,电脑只显示C盘我们应该怎么办
  9. Python中的 len() 是什么?如何使用 len() 函数查找字符串的长度
  10. vue-cli 项目启动输出 INFO Starting development server... 69o/o after emitting CopyPlugin