本文我们来梳理一下Spring的那些注解,如下图所示,大概从几方面列出了Spring的一些注解: 如果此图看不清楚也没事,请运行下面的代码输出所有的结果。 Spring目前的趋势是使用注解结合Java代码而不是配置来定义行为、属性、功能、规则和扩展点,因此梳理注解也是梳理Spring功能点的很好的方式,全面的梳理可以补足我们知识点的漏洞。
首先,我们来创建一个项目,使用SPRING INITIALIZR生成一个引入Spring各种组件的项目模板,然后引入如下工具包:
<dependency> <groupId>org.reflections</groupId> <artifactId>reflections</artifactId> <version>0.9.11</version> </dependency>通过这个反射工具包,我们可以创建一个Spring Boot应用程序,以一行代码打印出所有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特性),说白了就是给代码打上标签的能力。我们可以配置这个标签的保留阶段,仅源代码,源代码+字节码,源代码+字节码+运行时。通过引入注解,我们可以简单快速赋予代码生命力,大大提高代码可读性和扩展性。注解本身不具有任何能力,只是一个标签,但是我们可以定义各种标签然后实现各种标签处理器来对类、方法、属性甚至参数等进行功能扩展、功能开启、属性定义、行为定义、规则定义、关联处理、元数据定义等等。在实现各种框架的时候,我们经常会自定义标签方便框架使用者仅仅通过在合适的地方引入合适的注解来启用(或自定义)框架的一些能力并应用到我们的程序中。
不仅仅是框架的作者会大量使用注解,在之前的系列文章中我们也多次自定义注解,我们有通过定义@Metrics注解配合Spring AOP来为程序启动打点、日志、异常等功能,我们有通过定义@Sign注解配合Spring MVC的ResponseBodyAdvice进行数据签名功能,我们还经常会定义各种自定义注解配合Spring MVC的HandlerMethodArgumentResolver进行权限的校验等等功能。采用这种模式,我们的核心业务逻辑可以保持清晰干净,通过注解配合AOP赋予代码额外的能力。
你可能会说,注解还是有侵入性,我们需要耦合框架定义的那些注解,这个问题其实是无解的,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 MVC的各种注解对应了Spring MVC各方面的功能,下面我们来了解一下:
首先是三个定义了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 Cloud整齐划一通过各种EnableXXX注解开启某个功能,这里就不对这些注解进行说明了,使用Spring Boot组件的功能非常简单,基本就是引POM+EnableXXX+设置配置文件三部曲。
首先是 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的各种注解探寻一下背后实现的原理。