2018年不知不觉已经走到了尾声,你还在为分不清@Controller和@Restcontroller而烦恼吗?这篇博文从源码层面分析这两个注解,值得一读。

首先贴一张源码的图,对比一下,左边是@Controller的源码,右边是@RestController的。

如果觉得不清楚,看下面代码:

@Controller:

  1. @Target({ElementType.TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Component
  5. public @interface Controller {
  6. /**
  7. * The value may indicate a suggestion for a logical component name,
  8. * to be turned into a Spring bean in case of an autodetected component.
  9. * @return the suggested component name, if any (or empty String otherwise)
  10. */
  11. @AliasFor(annotation = Component.class)
  12. String value() default "";
  13. }

@RestController:

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Controller
  5. @ResponseBody
  6. public @interface RestController {
  7.  
  8. /**
  9. * The value may indicate a suggestion for a logical component name,
  10. * to be turned into a Spring bean in case of an autodetected component.
  11. * @return the suggested component name, if any (or empty String otherwise)
  12. * @since 4.0.1
  13. */
  14. @AliasFor(annotation = Controller.class)
  15. String value() default "";
  16.  
  17. }

显然,两个注解的源码里面都包含许多的注解:

@Controller的注解包括:@Target({ElementType.TYPE})、@Retention(RetentionPolicy.RUNTIME)、@Documented、@Component

@RestController的注解包括:@Target(ElementType.TYPE)、@Retention(RetentionPolicy.RUNTIME)、@Documented、@Controller、@ResponseBody

所以,源码的分析也就是对注解的分析。

内置注解和元注解

注解(也被称为元数据)为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便地使用这些数据。

Java SE5中有三种内置注解

@Override
表示当前的方法定义将覆盖超类中的方法
@Deprecated
如果程序中使用了注解为它的元素,那么编译器会发出警告
@SuppressWarnings
关闭不当的编译器警告信息
 
元注解是专门负责注解其他的注解,Java内置了四种元注解,专门负责新注解的创建,直接看这张表格(摘自Java编程思想):
@Target
表示该注解可以用于什么地方。可能的ElementType参数包括:
CONSTRUCTOR:构造器的声明
FIELD:域声明(包括enum实例)
LOCAL_VARIABLE:局部变量声明
METHOD:方法声明
PACKAGE:包声明
PARAMETER:参数声明
TYPE:类、接口(包括注解类型)和enum声明
@Retention
表示需要在什么级别保存该注解信息。可选的RetentionPolicy参数包括:
SOURCE:注解将在编译器丢弃
CLASS:注解在class文件中可用,但会被VM丢弃
RUNTIME:VM将在运行期也保留注解,因此可以通过反射机制读取注解的信息
@Documented 将此注解包含在Javadoc中
@Inherited 允许子类继承父类中的注解

现在,明白了@Target({ElementType.TYPE})、@Retention(RetentionPolicy.RUNTIME)、@Documented的作用,我们也可以自定义一个注解@Algorithms:

  1. /**
  2. *
  3. * Define a annotation named Algorithms
  4. *
  5. */
  6. @Target(ElementType.TYPE)
  7. @Retention(RetentionPolicy.SOURCE)
  8. public @interface Algorithms {
  9. String value() default "";
  10. }

@Component注解

@Component注解源码如下:

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Indexed
  5. public @interface Component {
  6.  
  7. /**
  8. * The value may indicate a suggestion for a logical component name,
  9. * to be turned into a Spring bean in case of an autodetected component.
  10. * @return the suggested component name, if any (or empty String otherwise)
  11. */
  12. String value() default "";
  13.  
  14. }

加了@Component注解,表明这是一个逻辑组件,告知Spring要为它创建bean。相当于xml配置文件中的 <bean id="" class=""/>的作用。

@AliasFor注解

@AliasFor注解是一个用于声明注解属性别名的注解,源码如下:

  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Target(ElementType.METHOD)
  3. @Documented
  4. public @interface AliasFor {
  5.  
  6. /**
  7. * Alias for {@link #attribute}.
  8. * <p>Intended to be used instead of {@link #attribute} when {@link #annotation}
  9. * is not declared &mdash; for example: {@code @AliasFor("value")} instead of
  10. * {@code @AliasFor(attribute = "value")}.
  11. */
  12. @AliasFor("attribute")
  13. String value() default "";
  14. /**
  15. * The name of the attribute that <em>this</em> attribute is an alias for.
  16. * @see #value
  17. */
  18. @AliasFor("value")
  19. String attribute() default "";
  20. /**
  21. * The type of annotation in which the aliased {@link #attribute} is declared.
  22. * <p>Defaults to {@link Annotation}, implying that the aliased attribute is
  23. * declared in the same annotation as <em>this</em> attribute.
  24. */
  25. Class<? extends Annotation> annotation() default Annotation.class;
  26.  
  27. }

@Controller中的@AliasFor(annotation = Component.class)说明@Controller是Component的一个别名,本质上还是一个Component,正如注释中所说“to be turned into a Spring bean in case of an  autodetected component.”,可以被扫描成一个bean。

同理,@RestController中的@AliasFor(annotation = Controller.class)说明@RestController是Controller的一个别名,是一个Controller,再本质一点说,是个Component,是个Spring bean。

@ResponseBody注解

  1. @Target({ElementType.TYPE, ElementType.METHOD})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface ResponseBody {
  5.  
  6. }

提到@ResponseBody注解,就不得不提一个名词:表述性状态转移(Representational State Transfer,REST)。

什么是表述性状态转移呢?拆开来看:

表述性:REST资源实际上可以用各种形式来表述,包括JSON、XML、HTML等;

状态:使用TEST的时候,我们更关注资源的状态而不是对资源采取的行为;

转移:以某种形式(例如JSON)从一个应用转换到另一个应用,例如从服务器到客户端。

简单讲,REST就是将资源的状态以最适合客户端或者服务器的形式从服务器转移到客户端(或者反过来)。

在Spring 4.0版本中,Spring支持借助@ResponseBody注解和各种HttpMethodConverter,替换基于视图的渲染方式,实现对REST的支持。当然Spring对REST的支持远不止这一种方式。

@ResponseBody注解告知Spring,要将返回的对象作为资源发送给客户端。这个过程跳过正常的模型/视图流程中视图解析的过程,而是使用Spring自带的各种Http消息转换器将控制器产生的数据转换为客户端需要的表述形式。如果客户端请求头的Accept表明他能接受“application/json”,并且Jackson JSON在类路径下面,那么处理方法返回的对象将交给MappingJacksonHTTPMessageConverter,并由他转换为JSON返回给客户端;如果客户端想要“text/html”格式,那么Jaxb2RootElementHttpMessageConverter将会为客户端产生XML响应。

当处理请求时候,@ResponseBody和@RequestBody是启用消息转换的一种简介和强大方式。但是,如果控制器里面的每个方法都需要信息转换功能的话,那么这些注解就会带有一定程度的重复性。

所以,Spring 4.0引入了@RestController注解,如果在控制器类上面使用@RestController注解,我们不必再为每个方法添加@ResponseBody注解,因为Spring会为该控制器下面的所有方法应用消息转换功能。这也是这个Controller之所以叫RestController的原因,正所谓见名知意。

总结

@RestController相当于@ResponseBody + @Controller一起作用。

如果控制器产生的结果希望让人看到,那么它产生的模型数据需要渲染到视图中,从而可以展示到浏览器中,使用@Controller。

如果控制器产生的结果不需要让人看到,那么它产生的数据经过消息转换器直接返回到浏览器,使用@RestController。

参考文献:

[1] Bruce Eckel. Java编程思想(第四版)[M]. 陈昊鹏译. 北京:机械工业出版社,2007.

[2] Craig Walls. Spring实战(第4版)[M]. 张卫滨译. 北京:人民邮电出版社,2016.

@Controller和@RestController源码解析的更多相关文章

  1. @GeneratedValue源码解析

    JPA要求每一个实体必须有且只有一个主键,而@GeneratedValue提供了主键的生成策略,这就是@GeneratedValue注解存在的意义.本文将浅析@GeneratedValue的源码. @ ...

  2. 实战录 | Kafka-0.10 Consumer源码解析

    <实战录>导语 前方高能!请注意本期攻城狮幽默细胞爆表,坐地铁的拉好把手,喝水的就建议暂时先别喝了:)本期分享人为云端卫士大数据工程师韩宝君,将带来Kafka-0.10 Consumer源 ...

  3. Heritrix 3.1.0 源码解析(三十七)

    今天有兴趣重新看了一下heritrix3.1.0系统里面的线程池源码,heritrix系统没有采用java的cocurrency包里面的并发框架,而是采用了线程组ThreadGroup类来实现线程池的 ...

  4. PureMVC(JS版)源码解析:总结

    PureMVC源码中设计到的11个类已经全部解析完了,回首想想,花了一周的时间做的这点事情还是挺值得的,自己的文字组织表达能力和对pureMVC的理解也在写博客的过程中得到了些提升.我也是第一次写系列 ...

  5. PureMVC(JS版)源码解析

    PureMVC(JS版)源码解析:总结   PureMVC源码中设计到的11个类已经全部解析完了,回首想想,花了一周的时间做的这点事情还是挺值得的,自己的文字组织表达能力和对pureMVC的理解也在写 ...

  6. spring MVC cors跨域实现源码解析

    # spring MVC cors跨域实现源码解析 > 名词解释:跨域资源共享(Cross-Origin Resource Sharing) 简单说就是只要协议.IP.http方法任意一个不同就 ...

  7. springMVC源码解析--ViewResolver视图解析器执行(三)

    之前两篇博客springMVC源码分析--ViewResolver视图解析器(一)和springMVC源码解析--ViewResolverComposite视图解析器集合(二)中我们已经简单介绍了一些 ...

  8. .Net Core缓存组件(Redis)源码解析

    上一篇文章已经介绍了MemoryCache,MemoryCache存储的数据类型是Object,也说了Redis支持五中数据类型的存储,但是微软的Redis缓存组件只实现了Hash类型的存储.在分析源 ...

  9. .Net Core缓存组件(MemoryCache)源码解析

    一.介绍 由于CPU从内存中读取数据的速度比从磁盘读取快几个数量级,并且存在内存中,减小了数据库访问的压力,所以缓存几乎每个项目都会用到.一般常用的有MemoryCache.Redis.MemoryC ...

随机推荐

  1. Java中的String类型

    1.基本类型和引用类型 在C语言里面,是有指针这么一个变量类型的,指针变量保存的就是所要指向内容的地址.在Java里面,没有了指针的这么个说法,而是换了一个词:引用类型变量. 先说Java里面的基本类 ...

  2. 洛谷 P1613 解题报告

    P1613 跑路 题目描述 小\(A\)的工作不仅繁琐,更有苛刻的规定,要求小\(A\)每天早上在\(6:00\)之前到达公司,否则这个月工资清零.可是小\(A\)偏偏又有赖床的坏毛病.于是为了保住自 ...

  3. H5之画布canvas小记,以及通过画布实现原子无规则运动

    我们知道html在h5出之前就仅仅只是一个标签,一个标记,语义化并不强,后来新增的标签如video,audio都是语义化更强(让人一看就懂是什么东西,反正我是这么理解的,一个div不代表着什么),本身 ...

  4. python_自定日历

    >>> from datetime import date>>> daysOfMonth=[31,28,31,30,31,30,31,31,30,31,30,31] ...

  5. MySQL松散索引扫描与紧凑索引扫描

    什么是松散索引? 答:实际上就是当MySQL 完全利用索引扫描来实现GROUP BY 的时候,并不需要扫描所有满足条件的索引键即可完成操作得出结果. 要利用到松散索引扫描实现GROUP BY,需要至少 ...

  6. 第二章:第一个Netty程序

    第一步:设置开发环境 • 安装JDK,下载地址http://www.oracle.com/technetwork/java/javase/archive-139210.html   • 下载netty ...

  7. 初识函数库libpcap

    由于工作上的需要,最近简单学习了抓包函数库libpcap,顺便记下笔记,方便以后查看 一.libpcap简介    libpcap(Packet Capture Library),即数据包捕获函数库, ...

  8. @Controller和@RestController之间的区别

    1. Controller, RestController的共同点 都是用来表示Spring某个类的是否可以接收HTTP请求 2. Controller, RestController的不同点 @Co ...

  9. ZooKeeper的使用---Java程序

    一.导入库 以下库存放在目录lib中: audience-annotations-0.5.0.jar jline-0.9.94.jar log4j-1.2.17.jar netty-3.10.6.Fi ...

  10. AJAX初步学习

    AJAX(Asynchronous JavaScript and XML)即异步的JavaScript与XML技术,指的是一套综合了多项技术的浏览器端网页开发技术.其实就是为了解决传统页面同步刷新,消 ...