设计模式——Spring注解编程模型
文章目录
- 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注解编程模型相关推荐
- Spring注解编程基石(一)
目录 Java注解 Java原生注解 元注解 Stereotype 注解 组合注解 组合注解实现的基础 @AliasFor 隐式别名 @AliasFor 和 @Inherited 区别 注解解析工具 ...
- Spring注解编程基石(二)
目录 辅助类 AttributeMethods AnnotationFilter RepeatableContainers MergedAnnotation接口 AnnotationsProcesso ...
- Spring注解编程基石(三)
目录 AnnotationUtils 源码分析 方法列表 方法源码 AnnotatedElementUtils 源码分析 Spring注解编程基石(一) Spring注解编程基石(二) Spring注 ...
- Spring注解编程基石(四)
目录 AnnotationsScanner 扫描方法 扫描source为Class方法 扫描source为Method方法 辅助方法 MergedAnnotationSelector MergedAn ...
- requestmapping注解作用_Java高级架构师-Spring 注解编程之注解属性别名与覆盖
欢迎关注头条号:Java小野猫 注解属性方法 在进入了解 Spring 注解属性功能之前,我们先看一个正常 Java 注解. 在注解中,属性方法与其他类/接口方法写法类似,但是存在一些区别. 注解属性 ...
- Spring核心编程思想
第01章:Spring Framework总览 (12讲) 01.课程介绍.mp4 02.内容综述.mp4 03.课前准备:学习三件套(工具.代码与大脑).mp4 ...
- [201903][Spring Boot 编程思想][核心篇][小马哥][著]
[201903][Spring Boot 编程思想][核心篇][小马哥][著] The Java Community Process(SM) Program https://jcp.org/en/ho ...
- 小马哥spring编程核心思想_极客小马哥Spring核心编程思想
42 | 依赖查找的今世前生:Spring IoC容器从Java标准中学到了什么? 43 | 单一类型依赖查找:如何查找已知名称或类型的Bean对象? 44 | 集合类型依赖查找:如何查找已知类型多个 ...
- spring controller 增加header字段forward_Spring 注解编程之模式注解
上篇文章研究 Spring XML Schema 扩展进制,这段时候一直研究 Spring 注解编程的原理.原本以为有了之前研究基础,可以很快理解注解编程原理.没想到这个过程非常困难,注解编程源码难度 ...
最新文章
- android air创建文件夹,安卓版Airdrop将上线:无需安装APP,轻松实现文件隔空投送...
- VS2015 将*.xaml.cs文件包裹在*.xaml文件下
- python 通登录银行_Python3 适合初学者学习的银行账户登录系统实例
- org.springframework.web.multipart.MultipartException: The current request is not a multipart request
- SAP CAM - Cloud Access Manager
- 【渝粤教育】电大中专电大中专职业健康与安全考试考核试题作业 题库
- (转ORCLE导入导出命令)
- 怎么使用hsqldb 和 mybatis 构造单元测试
- 按首字母排序(汉字、英文、数字)简单实现
- office办公哪个版本稳定好用
- Java常用英语单词(笔记整理)
- 最小包围球(附完整代码)
- 渗透测试如何学习? (*╹▽╹*) 信息收集 ~ 其一
- 80后,我们难忘的电视剧
- 2021年全球手机市场最大赢家除了苹果,还有OPPO系
- iOS IAP应用内购详细步骤和问题总结指南
- java epson_无法与Epson POS打印机通信
- 动态函数监控技术在缓冲区溢出检测中的应用
- html如何实现在线客服,在线客服.html
- linux系统log可以删吗,Linux 系统 /var/log/journal/ 垃圾日志清理
热门文章
- ssm基于javaweb的医疗健康知识管理系统设计与实现 毕业设计-附源码131903
- CUDA编程实现求解单源Bellman-Ford最短 路径算法
- 《Modelica教程》by Fritzson 导言部分
- SQL Server 常用查询练习
- 7-3 小孩子才做选择,大人全都要 (10 分)
- 祝福大家新年快乐,身体健康,工作顺利,万事如意!
- linux 网络错误代码,Linux版本登录提示网络错误
- 计算机硬盘只显示c盘,电脑只显示C盘我们应该怎么办
- Python中的 len() 是什么?如何使用 len() 函数查找字符串的长度
- vue-cli 项目启动输出 INFO Starting development server... 69o/o after emitting CopyPlugin