@Controller和@RestController源码解析

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

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

若是以为不清楚,看下面代码:json

@Controller:浏览器

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
     /**
      * The value may indicate a suggestion for a logical  component name,
      * to be turned into a Spring bean in case of an  autodetected component.
      * @return the suggested component name, if any (or empty  String otherwise)
      */
     @AliasFor(annotation = Component.class)
     String value() default "";
}
View Code

@RestController:服务器

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {

     /**
      * The value may indicate a suggestion for a logical  component name,
      * to be turned into a Spring bean in case of an  autodetected component.
      * @return the suggested component name, if any (or empty  String otherwise)
      * @since 4.0.1
      */
     @AliasFor(annotation = Controller.class)
     String value() default "";

}
View Code

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

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

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

因此,源码的分析也就是对注解的分析。spa

内置注解和元注解

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

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:

/**
*
* Define a annotation named Algorithms
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface Algorithms {
    String value() default "";
}

@Component注解

@Component注解源码以下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {

     /**
      * The value may indicate a suggestion for a logical  component name,
      * to be turned into a Spring bean in case of an  autodetected component.
      * @return the suggested component name, if any (or empty  String otherwise)
      */
     String value() default "";

}

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

@AliasFor注解

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface AliasFor {

     /**
      * Alias for {@link #attribute}.
      * <p>Intended to be used instead of {@link #attribute}  when {@link #annotation}
      * is not declared &mdash; for example: {@code  @AliasFor("value")} instead of
      * {@code @AliasFor(attribute = "value")}.
      */
     @AliasFor("attribute")
     String value() default "";
     /**
      * The name of the attribute that <em>this</em> attribute  is an alias for.
      * @see #value
      */
     @AliasFor("value")
     String attribute() default "";
     /**
      * The type of annotation in which the aliased {@link  #attribute} is declared.
      * <p>Defaults to {@link Annotation}, implying that the  aliased attribute is
      * declared in the same annotation as <em>this</em>  attribute.
      */
     Class<? extends Annotation> annotation() default  Annotation.class;

}

@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注解

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseBody {

}

提到@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.

相关文章
相关标签/搜索