本文咱们来梳理一下Spring的那些注解,以下图所示,大概从几方面列出了Spring的一些注解:

若是此图看不清楚也没事,请运行下面的代码输出全部的结果。
Spring目前的趋势是使用注解结合Java代码而不是配置来定义行为、属性、功能、规则和扩展点,所以梳理注解也是梳理Spring功能点的很好的方式,全面的梳理能够补足咱们知识点的漏洞。java
查找全部注解
首先,咱们来建立一个项目,使用SPRING INITIALIZR生成一个引入Spring各类组件的项目模板,而后引入以下工具包:web
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.11</version>
</dependency>
经过这个反射工具包,咱们能够建立一个Spring Boot应用程序,以一行代码打印出全部Spring框架的注解:spring
import org.reflections.Reflections;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.lang.annotation.Annotation;
@Component
public class ScanAnnotationRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
new Reflections("org.springframework")
.getSubTypesOf(Annotation.class)
.stream()
.map(clazz->clazz.getName())
.sorted()
.forEach(System.out::println);
}
}
输出结果这里就不给出了,下面咱们逐一进行梳理其中的一些重要注解。跨域
有关注解
Java的Annotation注解(相似于C#的Attribute特性),说白了就是给代码打上标签的能力。咱们能够配置这个标签的保留阶段,仅源代码,源代码+字节码,源代码+字节码+运行时。经过引入注解,咱们能够简单快速赋予代码生命力,大大提升代码可读性和扩展性。注解自己不具备任何能力,只是一个标签,可是咱们能够定义各类标签而后实现各类标签处理器来对类、方法、属性甚至参数等进行功能扩展、功能开启、属性定义、行为定义、规则定义、关联处理、元数据定义等等。在实现各类框架的时候,咱们常常会自定义标签方便框架使用者仅仅经过在合适的地方引入合适的注解来启用(或自定义)框架的一些能力并应用到咱们的程序中。cookie
不只仅是框架的做者会大量使用注解,在以前的系列文章中咱们也屡次自定义注解,咱们有经过定义@Metrics注解配合Spring AOP来为程序启动打点、日志、异常等功能,咱们有经过定义@Sign注解配合Spring MVC的ResponseBodyAdvice进行数据签名功能,咱们还常常会定义各类自定义注解配合Spring MVC的HandlerMethodArgumentResolver进行权限的校验等等功能。采用这种模式,咱们的核心业务逻辑能够保持清晰干净,经过注解配合AOP赋予代码额外的能力。app
你可能会说,注解仍是有侵入性,咱们须要耦合框架定义的那些注解,这个问题实际上是无解的,100%无侵入性也表明了可读性的下降,代码的功能和能力应当聚合在一块儿,这也就是为何Spring如今也不建议采用XML来作配置。Java核心类库并无什么注解,好在Spring已经有了大量注解,而Spring也变为了Java开发的标准,因此其实咱们不少时候若是但愿本身的框架(RPC啥的)彻底没有侵入性的话能够借用Spring的那些注解@Autowired、@Controller、@Service等注解,配合各类包的规范其实咱们能够对目标元素的功能识别个八九不离十,彻底有可能实现0侵入的功能加强。负载均衡
有关如何实现自定义注解不赘述,这里咱们简单回顾一下几个元注解(注解的注解):框架
- @Documented:将会在被此注解注解的元素的javadoc文档中列出注解,通常都打上这个注解没坏处
- @Target:注解能被应用的目标元素,好比类、方法、属性、参数等等,须要仔细思考
- @Retention:仅在源码保留,仍是保留到编译后的字节码,仍是到运行时也去加载,超过90%的应用会在运行时去解析注解进行额外的处理,因此大部分状况咱们都会设置配置为RetentionPolicy.RUNTIME
- @Inherited:若是子类没有定义注解的话,能自动从父类获取定义了继承属性的注解,好比Spring的@Service是没有继承特性的,可是@Transactional是有继承特性的,在OO继承体系中使用Spring注解的时候请特别注意这点,理所固然认为注解是能被子类继承的话可能会引发没必要要的Bug,须要仔细斟酌是否开启继承
- @Repeatable:Java 8 引入的特性,经过关联注解容器定义可重复注解,小小语法糖提升了代码可读性,对于元素有多个重复注解实际上是很常见的事情,好比某方法能够是A角色能够访问也能够是B角色能够访问,某方法须要定时任务执行,要在A条件执行也须要在B条件执行
- @Native:是否在.h头文件中生成被标记的字段,除非原生程序须要和Java程序交互,不然不多会用到这个元注解
如今咱们来从几个方面逐一温习一下Spring的那些经常使用的值得关注的注解。
Spring核心注解
- 首先来看一下各类stereotype:按分类定义了由Spring管理的各类组件,@Controller定义表现层组件,@Service定义业务逻辑层组件,@Repository定义数据访问层资源库组件,@Component定义其它组件(好比访问外部服务的组件),以前也说过了随着这些注解功能无区别,可是对组件进行合适的分类意义重大,不只仅增长可读性并且方便咱们经过AOP对不一样类型的组件进行更多自动加强
- 再来看看IOC相关的一些注解:@Autowired自动装配不用多说了;@Required用于在setter方法标记属性值须要由Spring进行装配,对于目前版本的Spring这个注解已经废弃,如今Spring更推荐使用构造方法注入;@Qualifier用于经过给Bean定义修饰语来注入相应的Bean,和@Autowired一块儿使用至关于@Resource的效果,固然还有一种常见用法是嵌入其它注解用于对Bean进行区分,而后配合@Autowired一块儿使用,参见后面提到的Spring Cloud的@LoadBalanced注解;@Value用于注入属性配置或SpEL表达式(前者是咱们常见用法,后者能够从其它对象获取值,功能更强大一点);@Lookup能够实现方法注入,若是咱们的类是单例的,可是又但愿Spring注入的依赖的对象是Prototype生命周期(每次new一个出来)的,这个时候能够经过此注解进行方法注入
- 而后来看一下有关事务的几个注解:@EnableTransactionManagement用于开启事务管理,使用Spring Boot若是引入Spring Data的话不须要手动开启(不过建议你们在使用事务的时候仍是经过日志来验证事务管理是否生效);@Transactional你们都知道用于开启事务以及设置传播性、隔离性、回滚条件等;@TransactionalEventListener用于配置事务的回调方法,能够在事务提交前、提交后、完成后以及回滚后几个阶段接受回调事件
- @Order注解能够设置Spring管理对象的加载顺序,在以前介绍AOP的文章中咱们看到有的时候咱们必须经过设置合理的@Order来合理安排切面的切入顺序避免一些问题,还有在一些业务场景中,咱们每每会去定义一组相似于Filter的@Component,而后会从容器得到一组Bean,这个时候业务组件的运行顺序每每会比较重要,也能够经过这个方式进行排序
- @AliasFor注解能够设置一组注解属性相互做为别名,对于有歧义的时候会使代码更清晰,此外还有一个用途是建立复合注解,Spring MVC的@GetMapping注解就是基于@RequestMapping这个注解建立的复合注解,咱们能够很方便得经过这种方式来实现注解的继承
Spring上下文注解
- 首先来看一下配置相关的一些注解:@Configuration用于标注配置类,启用Java配置方式的Bean配置;@Bean用于配置一个Bean;@ComponentScan(@ComponentScans用于配置一组@ComponentScan,Java 8能够直接使用重复注解特性配置多个@ComponentScan)用于扫描包方式配置Bean;@PropertySource以及 @PropertySources用于导入配置文件;@Conditional用于设置关联的条件类,在合适的时候启用Bean的配置(Spring Boot自动配置根基);@Import用于导入其它配置类; @ImportResource用于导入非Java配置方式的XML配置;@Profile用于指定在合适的Profile下启用配置;@Lazy用于告知容器延迟到使用的时候实例化Bean(默认状况下容器启动的时候实例化Bean来检查全部的问题);@Description用于给Bean设置描述;@Scope用于设置Bean的生命周期;@Primary用于在定义了多个Bean的时候指定首选的Bean
- 其它一些注解包括:@EventListener用于设置回调方法监听Spring制定的以及自定义的各类事件;@EnableAspectJAutoProxy用于开启支持AspectJ的 @Aspect切面配置支持,使用Spring Boot引入了AOP启动器的话不须要显式开启
Spring Web注解
Spring MVC的各类注解对应了Spring MVC各方面的功能,下面咱们来了解一下:ide
- 首先是三个定义了Bean特殊生命周期的复合注解:@RequestScope、@SessionScope和 @ApplicationScope。在Web应用中,咱们可能须要Bean跟随请求、会话和应用程序的声明周期来进行建立,这个时候能够直接使用这三个快捷的复合注解
- 接下去能够看到各类 @XXXMapping的注解,分别用于配置HandlerMethod匹配到不一样的Http Method,固然不使用这些快捷的注解也是能够的,直接使用@RequestMapping而后手动设置method
- @ResponseStatus能够用到方法上也能够用到异常上,前者会直接使请求获得指定的响应代码或缘由(能够配合@ExceptionHandler使用),后者能够实现遇到指定异常的时候给出指定的响应代码或缘由,@ResponseBody咱们实现Restful接口的时候(@RestController)最经常使用了,把返回内容(序列化后)输出到请求体
- Spring MVC给了咱们各类注解方便咱们从HTTP请求各类地方获取参数,@RequestBody从请求体(处理复杂数据,好比JSON),@RequestHeader从请求头,@CookieValue从cookie中,@SessionAttribute从会话中,@RequestAttribute从请求的Attribute中(好比过滤器和拦截器手动设置的一些临时数据),@RequestParam从请求参数(处理简单数据,键值对),@PathVariable从路径片断,@MatrixAttribute矩阵变量容许咱们采用特殊的规则在URL路径后加参数(分号区分不一样参数,逗号为参数增长多个值)
- @ControllerAdvice是一个重要注解,容许咱们在集中的地方配置控制器(有@RequestMapping的方法)相关的加强(@RestControllerAdvice也是差很少的,只是至关于为@ExceptionHandler加上了@ResponseBody)。那么能够应用哪些加强呢?首先是能够用 @ExceptionHandler进行统一的全局异常处理;第二是 @InitBinder用来设置WebDataBinder,WebDataBinder用来自动绑定前台请求参数到Model中;第三是 @ModelAttribute让全局的@RequestMapping都能得到在此处设置的键值对。固然,这里说的@InitBinder和@ExceptionHandler也能够不定义在@ControllerAdvice内部(做为全局开启),定义在Controller内部应用到某个Controller也是能够的
- 其它还有一些注解好比:@CrossOrigin能够用到Controller或Method上(须要配合@RequestMapping)设置细粒度的跨域行为
在以前的文章中咱们也提到,对于Spring MVC,定义本身的注解应用到参数、方法、控制器上,配合HandlerMethodArgumentResolver、XXAdvise、以及Interceptor实现具体的功能来使用太太常见了,几乎全部的非业务横切关注点,咱们都不该该在方法实现中重复任何一行代码。
Spring Boot注解
- 来看一下上下文相关的注解:@ConfigurationProperties很经常使用(配合 @EnableConfigurationProperties注解来设置须要启用的配置类),用来自定义配置类和配置文件进行关联;@DeprecatedConfigurationProperty用于标记废弃的配置以及设置替代配置和告知废弃缘由;@ConfigurationPropertiesBinding用于指定自定义的转换器用于配置解析的时的类型转换; @NestedConfigurationProperty用于关联外部的类型做为嵌套配置类
- 再看看自动配置相关的注解,自动配置是Spring Boot最重要的特性,在以前的系列文章中我有提到一个观点,IOC是好事情,可是把组件内部的一些默认配置以及组件和组件的组装交给外部用户来配置实际上是不合理的,组件应当能够自动进行自我配置实现开箱急用,只有须要自定义组件的时候才要求外部来进行个性化配置:@EnableAutoConfiguration注解能够启用自动配置,Spring Boot应用程序通常咱们会直接使用复合注解@SpringBootApplication;@AutoConfigureOrder(值越小优先级越高)、@AutoConfigureAfter、@AutoConfigureBefore用于设置自动配置类加载顺序,以及精确控制加载依赖关系,有的时候咱们的自动配置须要相互依赖或者会相互干扰,须要手动调节
- 最后来看一下十几种配置条件,用好这些注解是实现完善的自动配置的关键:@ConditionalOnBean用于仅当容器中已经包含指定的Bean类型或名称时才匹配条件;@ConditionalOnClass仅当classpath上存在指定类时条件匹配;@ConditionalOnCloudPlatform仅当指定的云平台处于活动状态时条件匹配;@ConditionalOnExpression依赖于SpEL表达式的值的条件元素的配置注解;@ConditionalOnJava基于应用运行的JVM版本的条件匹配;@ConditionalOnJndi基于JNDI可用和能够查找指定位置的条件匹配;@ConditionalOnMissingBean仅当容器中不包含指定的Bean类型或名称时条件匹配;@ConditionalOnMissingClass仅当classpath上不存在指定类时条件匹配;@ConditionalOnNotWebApplication 仅当不是WebApplicationContext(非Web项目)时条件匹配,对应 @ConditionalOnWebApplication;@ConditionalOnProperty是检查指定的属性是否具备指定的值;@ConditionalOnResource表示仅当 classpath 上存在指定资源时条件匹配;@ConditionalOnSingleCandidate仅当容器中包含指定的Bean类而且能够判断只有单个候选者时条件匹配。其实全部这些实现原理都是扩展SpringBootCondition抽象类(实现以前提到的Condition接口),咱们彻底能够实现本身的条件注解(配合 @Conditional注解关联到本身实现的SpringBootCondition)
Spring Cloud注解
在介绍本系列文章的第一篇中咱们就提到了,Spring Cloud整齐划一经过各类EnableXXX注解开启某个功能,这里就不对这些注解进行说明了,使用Spring Boot组件的功能很是简单,基本就是引POM+EnableXXX+设置配置文件三部曲。spring-boot
- 首先是 Netflix包下的一些注解,各类EnableXXX就不说了,参考前一篇文章,以前没介绍过 @RibbonClient,这个注解用来为负载均衡客户端作一些自定义的配置,能够进一步配置或自定义从哪里获取服务端列表、负载均衡策略、Ping也就是服务鉴活策略等等
- client包下的 @SpringCloudApplication以前文章中咱们也没有使用到,这是一个复合注解就是 @SpringBootApplication+ @EnableDiscoveryClient+ @EnableCircuitBreaker,Spring Cloud那堆东西不少,仍是本身亲手定义一个一个功能的注解来的踏实; @LoadBalanced注解用于和RestTemplate配合使用构成一个负载均衡的Http客户端,实现原理上其实这个注解是一个@Qualifier注解,Spring会为全部@LoadBalanced的RestTemplate加入一个LoadBalancerInterceptor(实现ClientHttpRequestInterceptor)实现负载均衡
- sleuth包下面的注解和链路跟踪相关,比较经常使用的是经过 @SpanName手动设置span的名称,其它注解对于业务开发并不经常使用
总结
好了,写了本文我发现我看到@已经Markdown的**就眼花,请点赞支持。本文咱们经过代码打印出了大部分Spring相关的注解,你也能够经过这个方式熟悉其它框架的注解(毕竟注解是框架赋予咱们各类便捷功能的一个重要入口,对注解了解个八九成也每每能够对框架赋予咱们的丰富功能了解六七成)。而后咱们梳理了一下Spring相关的各类注解,其中主要须要关注的是几方面:
- 元注解,也就是注解的注解
- Spring容器相关的一些注解,包括@Qualifier、@AliasFor、@Order等看似不重要但其实很重要的注解
- Spring Java配置相关的一些注解,包括条件注解
- Spring Boot自动配置相关的一些注解
- 不少注解能够同时应用到类型、方法、参数上,有的时候应用到不一样的地方做用会略微不同,这个须要重点关注
咱们知道注解其实只是一个标识,注解如何起做用背后的实现原理仍是比较多样的,你能够进一步结合本文介绍的Spring的各类注解探寻一下背后实现的原理。