SpringBoot系列(8):SpringBoot中的MVC支持【组件型注解、请求和参数型注解】详解(令人发指篇)

目录

前言

1. 组件型注解

2. 请求和参数型注解

1、组件型注解

2、请求和参数型注解

2.1 @RestController

2.2 @RequestMapping

2.3 @PathVariable

2.4 @RequestParam

2.5 @RequestBody

3、总结


前言

SpringBoot的MVC 支持主要来自于实地项目开发中我们最常用的几个注解,这些注解可以分为两类:

1. 组件型注解

  • @Component:没有明确的角色定,在类定义之前添加@Component注解,它会被spring容器识别,并转为bean。
  • @Repository:用于数据访问层(DAO层)的注解 (特殊的@Component)
  • @Service:用于对业务逻辑层的注解, (特殊的@Component)
  • @Controller:用于表现层(MVC-->SpringMVC)注解 , (特殊的@Component)

上边的四种注解均注解在类上的,被注解的类将被spring初始化声明为一个实例Bean,然后由容器进行统一管理。

2. 请求和参数型注解

  • @RestController
  • @RequestMapping
  • @PathVariable
  • @RequestParam
  • @RequestBody

接下来,本节内容将逐一总结上边这两种类型注解常用的使用方式及特点。 


1、组件型注解

在实地应用场景中,这四类注解一般多用于业务Bean的配置。

接下来,对于组件型注解使用方式,这里通过一个栗子(包括实体类、持久层、业务层、Controller)来介绍一下。

pom.xml依赖配置:

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.3.2</version>
</dependency>
<!-- 分页插件 -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.2.12</version>
</dependency>
<dependency>
    <groupId>javax.persistence</groupId>
    <artifactId>persistence-api</artifactId>
    <version>1.0.2</version>
</dependency>

application.yml中mybatis相关配置:

#application-level config
server:
  port: 8080
spring:
  #datasource config
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
    username: root
    password: root
    druid:
      initialSize: 5
      minIdle: 5
      maxActive: 20
      maxWait: 60000
      timeBetweenEvictionRunsMillis: 60000
      minEvictableIdleTimeMillis: 300000
      validationQuery: SELECT 1
      testWhileIdle: true
      testOnBorrow: true
      testOnReturn: false
      poolPreparedStatements: true
      maxPoolPreparedStatementPerConnectionSize: 20
      filters: stat,wall
      connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
      stat-view-servlet:
        allow: 127.0.0.1
#mybatis config
mybatis:
  type-aliases-package: com.hl.magic.mvc.model
  mapper-locations: classpath:/mybatis-mappings/*
  configuration:
    mapUnderscoreToCamelCase: true
#mybatis-pagehelper config
pagehelper:
  pagehelperDialect: mysql
  reasonable: true
  support-methods-arguments: true
  #params: count=countSql

实体类:

/**
 * 实体类--系统用户
 */
public class SysUser extends BaseModel {

    /**
     * ID标识
     */
    @Id
    @Column(name = "ID")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private String id;

    @Column(name = "USER_NAME")
    private String userName;

    private String password;

    @JsonFormat(pattern = "yyyy-MM-dd")
    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
    private Date birthday;

    /**省略setter和getter方法*/
}

DAO层Mapper接口:

@Repository
public interface SysUserMapper {

    /**
     * 根据角色ID查询角色信息
     */
    SysUser selectById(String id);
}

注解@Repository的源码定义:

这个注解表明配置了该注解的类是一个DAO持久层的接口类,可以看到它的注解包含@Document。

同时,该注解类提供一个默认的引入别名注解的value()方法,该方法返回值表示对逻辑组件名称的别名定义,spring容器在自动检测组件的情况下会将引入注解@Responsitory的类对象转换为Spring bean实例。

DAO层Mapper接口映射文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<!--
    命名空间namespace,做sql隔离
    mybatis映射文件中的命名空间namespace的值就是需要配置成Mapper接口的全限定路径名称FNQ

    映射文件:由Mapper接口和XML文件组成。
    mybatis内部就是通过namespace将接口和xml关联起来,可配置以下内容:
        1.描述映射规则
        2.提供SQL语句,并可以配置SQL参数类型、返回类型、缓存刷新
        3.配置缓存
        4.提供动态SQL
    作用:将SQL查询到的结果映射为一个实体类,或者将该实体类的数据插入到数据库中,并定义一些缓存等
-->
<mapper namespace="com.hl.magic.mvc.dao.SysUserMapper">
    <resultMap id="baseMap" type="SysUser">
        <id property="id" column="ID" />
        <result property="userName" column="USER_NAME"/>
        <result property="password" column="PASSWORD" />
        <result property="birthday" column="BIRTHDAY" />
    </resultMap>
    <!--按id查询用户信息
            id:sql语句唯一标识
            parameterType:指定传递给SQL的参数类型
            resultType:指定返回结果集类型[指向POJO持久化类]
            resultMap:设置返回值的类型和映射关系。可通过在resultMap中配置property属性和column属性的映射。
            #{}:占位符,如果传入参数的是(String、long、double、int等)基本类型,那么#{}中的变量名称可以随便写。
    -->
    <select id="selectById" parameterType="String" resultMap="baseMap">
        SELECT
            *
        FROM
            sys_user A
        WHERE
            A.ID = #{id}
    </select>
</mapper>

 业务层接口:

public interface ISysUserService {

    /**
     * 根据角色ID查询角色信息
     */
    SysUser selectById(String id);
}

业务层接口实现类:

@Service
public class SysUserService implements ISysUserService {

    @Autowired
    private SysUserMapper sysUserMapper;

    public SysUserService() {
        System.out.println("SysUserService实例化");
    }

    @Override
    public SysUser selectById(String id) {
        return sysUserMapper.selectById(id);
    }
}

注解@Service的源码定义:

这个注解表明配置了该注解的类是一个Service层的类,它的注解包含@Document。

同样的,注解类Service也提供一个默认的引入别名注解的value()方法,该方法返回值表示对逻辑组件名称的别名定义,spring容器在自动检测组件的情况下会将引入注解@Service的类对象转换为Spring bean实例。

Controller层:

@Controller
public class SysUserController {

    @Autowired
    private ISysUserService sysUserService;

    @GetMapping("/userInfo")
    public ResponseMessage getUser(String id) {
        SysUser sysUser = sysUserService.selectById(id);
        return new ResponseMessage(ResponseStatus.SUCCESS.getStatus(),
            ResponseStatus.SUCCESS.getMessage(), sysUser);
    }
}

注解@Controller的源码定义:

这个注解表明配置了该注解的类是一个“控制器”类,它的注解包含@Document。该注释是{@link Component @Component}的一个专门化,允许通过类路径扫描自动检测实现类,常与@RequestMapping结合使用。

如同持久层和业务侧注解类一样,Controller这个注解类也提供了一个默认的引入别名注解的value()方法,该方法返回值表示对逻辑组件名称的别名定义,spring容器在自动检测组件时会将引入注解@Controller的类对象转换为Springbean实例。

上边三类注解均引入了注解@Component,看下它的源码定义:

注解@Component表明该类将会作为组件类,并告诉spring要为这个类创建一个实例Bean。

通过上边的栗子,我们可以清晰的看到组件型的四类使用方式、特点、源码定义。它们都是注解在类上的,被注解的类将被spring初始化为一个实例bean,然后由容器进行统一管理。


2、请求和参数型注解

2.1 @RestController

@RestController是 Spring Boot新增的一个注解,先看下其源码定义:

根据源码定义,@RestController注解包含了@Controller@ResponseBody注解。关于@Controller注解,这里不再赘述。

举个@Controller注解使用栗子:

@Controller
public class SysUserController {

    @GetMapping(value = "/userInfo1")
    public @ResponseBody
    ResponseMessage getUser() {
        User sysUser = new User();
        sysUser.setId(UUID.randomUUID().toString().replace("-",""));
        sysUser.setUserName("小明");
        sysUser.setPassword("123456");
        sysUser.setBirthday(new Date());
        return new ResponseMessage(ResponseStatus.SUCCESS.getStatus(),
            ResponseStatus.SUCCESS.getMessage(), sysUser);
    }
}

postman测试结果:

 

@RestController注解是从Spring 4.0以后产生的,用来将json/xml数据发送到前台页面,而不是返回视图页面。它将视图层返回的数据结构转换为json格式。所以,使用注解@RestController也就相当于同时使用了注解@Controller和@ResponseBody的结合体,使用 @RestController后就不用再使用@Controller了。

注意:如果是前后端分离,不用模板渲染的话,比如Thymeleaf,这种情况下是可以直接使用@RestController将数据以json格式传给前端,前端拿到之后解析;但如果不是前后端分离,需要使用模板来渲染的话,一般Controller中都会返回到具体的页面,那么此时就不能使用@RestController了。

举个@RestController栗子:

@RestController
public class SysUserRestController {

    @GetMapping(value = "/userInfo2")
    public ResponseMessage select() {
        User sysUser = new User();
        sysUser.setId(UUID.randomUUID().toString().replace("-",""));
        sysUser.setUserName("小明");
        sysUser.setPassword("123456");
        sysUser.setBirthday(new Date());
        return new ResponseMessage(ResponseStatus.SUCCESS.getStatus(),
            ResponseStatus.SUCCESS.getMessage(), sysUser);
    }
}

其实是需要返回到 user.html 页面的,如果使用 `@RestController` 的话,会将 user 作为字符串返回的,所以这时候我们需要使用 `@Controller` 注解。

postman测试结果:

小结:

 对比上边两个小栗子及测试结果,我们可以知道@Controller和@RequestController的联系和区别:

  • 注解@RestController相当于注解@Controller + @ResponseBody合在一起的作用。
  •  
  • 使用注解@Controller时如果需要返回到指定页面,@Controller可配合视图解析器InternalResourceViewResolver,在对应的方法上,视图解析器可以解析return 的jsp,html页面,并且跳转到相应页面。
  • 使用注解@Controller标注的controller,如果需要返回json格式数据,则需要在方法返回前添加@ResponseBody。
  •  
  • 使用注解@RestController时,如果需要返回json数据,则不需要在方法返回前加@ResponseBody注解。
  • 使用注解@RestController标注的controller,这个controller中的所有方法都会返回数据,不再跳转页面就不能返回jsp,html页面,配置的视图解析器InternalResourceViewResolver不起作用,图解析器无法解析jsp,html页面。

2.2 @RequestMapping

注解@RequestMapping是一个用来处理请求地址映射的注解。它可以用于类上,也可以用于方法上。

  • 注解@RequestMapping作用在类的级别上时,会将一个特定请求或者请求模式映射到一个控制器之上,表示类中的所有响应请求的方法都是以该地址作为父路径。
  • 注解@RequestMapping作用在方法的级别时,表示进一步指定到处理方法的映射关系。

该注解有6个属性,一般在项目中比较常用的有三个属性:value、method 和 produces。

  • value 属性:指定请求的实际地址,value 可以省略不写。
  • method 属性:指定请求的类型,主要有 GET、PUT、POST、DELETE,默认为 GET。
  • produces属性:指定返回内容类型,如 produces = "application/json; charset=UTF-8"。

注解@RequestMapping的源码释义:

/**
 * 用于将web请求映射到请求处理类中的方法的注释
 * 具有灵活的方法签名。
 *
 * Spring MVC和Spring WebFlux都在各自的模块和包结构中通过
 * 一个{@code RequestMappingHandlerMapping}和{@code RequestMappingHandlerAdapter}来支持这个注释。
 * 若要获取受支持的处理程序方法参数和每个中的返回类型的确切列表,请使用参考文件链接如下:
 *<ul>
 * <li>Spring MVC
 * <a href="https://docs.spring.io/spring/docs/current/spring-framework-
 *   reference/web.html#mvc-ann-arguments">Method Arguments</a>
 * and
 * <a href="https://docs.spring.io/spring/docs/current/spring-framework-
 *   reference/web.html#mvc-ann-return-types">Return Values</a>
 * </li>
 * <li>Spring WebFlux
 * <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/web-
 *   reactive.html#webflux-ann-arguments">Method Arguments</a>
 * and
 * <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/web-
 *   reactive.html#webflux-ann-return-types">Return Values</a>
 * </li>
 * </ul>
 *
 * 这个注释既可以在类中使用,也可以在方法级使用。
 * 在大多数情况下,在方法级别上,应用程序更喜欢使用HTTP方法特定的变体之一
 * {@link GetMapping @GetMapping},
 * {@link PostMapping @PostMapping},
 * {@link PutMapping @PutMapping},
 * {@link DeleteMapping @DeleteMapping},
 * 或者{@link PatchMapping @PatchMapping}。
 * 
 * 当使用控制器接口时(例如,对于AOP代理),确保始终将所有的映射注释
 * 例如{@code @RequestMapping}和{@code @SessionAttributes}—放在控制器接口上,而不是放在实现类上。
 *
 * @Mapping 这个注解表示web映射注释的元注释。
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {

       /**
	* 路径映射uri(例如“/ myPath.do”)。
        * 也支持ant样式的路径模式(例如“/ myPath / *,”)。
        * 在方法级别,相对路径(例如在类型级别表示的主映射中支持“edit.do”)。
        * 路径映射uri可能包含占位符(例如“/ ${connect}”)。
        * 在类型级别和方法级别都支持!
        * 当在类型级别使用时,所有方法级别的映射都继承这个主映射,
        * 为特定的处理程序方法缩小其范围。
	*/
	@AliasFor("path")
	String[] value() default {};

       /**
        * 要映射的HTTP请求方法,缩小主映射:
        * GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE。
        * 在类型级别和方法级别都支持!
        * 当在类型级别使用时,所有的方法级别映射都继承了这个HTTP方法限制
        * (即在处理程序方法解析之前检查类型级别限制)。
        */
	@AliasFor("value")
	String[] path() default {};

       /**
	* 要映射的HTTP请求方法,缩小主映射:
        * GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE。
        * 在类型级别和方法级别都支持!
        * 在类型级别使用时,所有方法级别的映射都将继承
        * 此HTTP方法限制(即在处理程序方法解析之前检查类型级别限制)。
	*/
	RequestMethod[] method() default {};

       /**
        * 映射请求的参数,缩小主映射。
        * 适用于任何环境的相同格式:一个“myParam=myValue”样式表达式序列,
        * 只有在发现每个这样的参数都具有给定的值时才映射请求。
        * 表达式可以通过使用“!”="操作符,如"myParam!=myValue"。
        * 也支持“myParam”样式表达式,这些参数必须出现在请求中(允许有任何值)。
        * 最后,”!“myParam”样式表达式表示指定的参数不应该出现在请求中。
        * 在类型级别和方法级别都支持!
        * 当在类型级别使用时,所有方法级别的映射都会继承这个参数限制
        * (即在解析处理程序方法之前检查类型级别的限制)。
        * 参数映射被认为是在类型级别强制执行的限制。
        * 主路径映射(即指定的URI值)仍然必须惟一地标识目标处理程序,
        * 参数映射只是表示调用处理程序的先决条件。
        */
	String[] params() default {};

	/**
	 * 映射请求的标头,缩小主映射。
         * 对于任何环境都是相同的格式:一个“My-Header=myValue”样式表达式序列,
         * 只有在发现每个这样的header具有给定值时才映射请求。
         * 表达式可以通过使用“!”="操作符,如"My-Header!=myValue"。
         * 还支持“My-Header”样式的表达式,这样的header必须出现在请求中(允许有任何值)。
         * 最后,”!My-Header"样式表达式表示指定的标头不应该出现在请求中。
         * 还支持媒体类型通配符(*),用于Accept和Content-Type等标头。例如,
         * @RequestMapping(value =" /something", headers =" content-type=text/*") 
         * 将匹配请求的内容类型为"text/html", "text/plain"等。
         * 在类型级别和方法级别都支持!
         * 在类型级别使用时,所有方法级别的映射都将继承
         * 这个头限制(即在处理程序方法解析之前检查类型级别限制)。
	 */
	String[] headers() default {};

	/**
	 * 映射请求的可使用媒体类型,缩小主映射。
         * 该格式是单个媒体类型或媒体类型序列,
         * 只有在{@code Content-Type}匹配这些媒体类型之一时才映射请求。
         * 例子:
         * < pre类=“代码”>
         * = "text/plain"
         * = {"text/plain", "application/*"}
         * < / pre >
         * 表达式可以通过使用“!”运算符来否定,比如在“!”text/plain”,
         * 它使用除“text/plain”之外的{@code Content-Type}匹配所有请求。
         * 在类型级别和方法级别都支持!
         * 当在类型级别使用时,所有方法级别的映射都会覆盖此限制。
	 */
	String[] consumes() default {};

	/**
	 * 映射请求的可生成媒体类型,缩小主映射。
         * 格式是单个媒体类型或一系列媒体类型,只有当{@code Accept}
         * 匹配其中一个媒体类型时才映射请求。
         * 例子:
         * < pre类=“代码”>
         * produces = "text/plain"
         * produces = {"text/plain", "application/*"}
         * produces = MediaType.APPLICATION_JSON_UTF8_VALUE
         * < / pre >
         * 它影响实际写的内容类型,例如产生一个带有UTF-8编码的JSON响应,
         * {@link org.springframework.http。应该使用MediaType#APPLICATION_JSON_UTF8_VALUE}。
         * 表达式可以通过使用"!"运算符来否定,如"!"text/plain”,
         * 它用除“text/plain”之外的{@code Accept}匹配所有请求。
         * 在类型级别和方法级别都支持!
         * 当在类型级别使用时,所有方法级别的映射都会覆盖此限制。
	 */
	String[] produces() default {};
}

虽然看着上边源码中注释内容很复杂,实际上注解@RequestMapping使用却很简单。

为了区分@RequestMapping和@GetMapping这两个注解的用法特点,举个栗子先:

@RestController
@RequestMapping("/test")
public class SysUserRestController {

    @RequestMapping(value = "/userInfo1",method = RequestMethod.GET)
    public ResponseMessage select1() {
        User user = new User();
        user.setId(UUID.randomUUID().toString().replace("-",""));
        user.setUserName("小明");
        user.setPassword("123456");
        user.setBirthday(new Date());
        user.setRemark("select1()这个方法使用了注解@RequestMapping");
        return new ResponseMessage(ResponseStatus.SUCCESS.getStatus(),
            ResponseStatus.SUCCESS.getMessage(), user);
    }

    @GetMapping(value = "/userInfo2")
    public ResponseMessage select2() {
        User user = new User();
        user.setId(UUID.randomUUID().toString().replace("-",""));
        user.setUserName("小明");
        user.setPassword("123456");
        user.setBirthday(new Date());
        user.setRemark("select2()这个方法使用了注解@GetMapping");
        return new ResponseMessage(ResponseStatus.SUCCESS.getStatus(),
            ResponseStatus.SUCCESS.getMessage(), user);
    }
}

启动测试项目,postman测试结果:

比较上边测试结果,可以发现@RequestMapping和@GetMapping这两个注解的用法略微有点区别,预期结果差别不大。

小结:

  • 针对GET、POST、 PUT、DELETE等四种不同的请求方式,是有相应注解的,不用每次在 `@RequestMapping` 注解中加 method 属性来指定,上面的 GET 方式请求的@RequestMapping(value = "/userInfo3",method = RequestMethod.GET)可以直接使用注解@GetMapping("/userInfo3") ,其效果一样。
  • 同样的,对于PUT方式、POST方式和DELETE方式对应的注解分别为@PutMapping、@PostMapping和DeleteMapping。

2.3 @PathVariable

@PathVariable注解主要是用来获取url参数,Spring Boot支持restfull风格的url,比如一个GET请求携带一个参数id过来,我们将id作为参数接收,可以使用@PathVariable注解。

【1】注解@PahVariable的源码定义:

/**
 * 该注释表示方法参数应该绑定到一个URI模板变量。
 * 支持{@link RequestMapping}注释的处理程序方法。
 * 如果方法参数是{@link java.util。然后,地图将填充所有路径变量名和值。
 */
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PathVariable {

	/**
	 * 默认的以name值作为别名。
	 */
	@AliasFor("name")
	String value() default "";

	/**
	 * 要绑定到的路径变量的名称。
	 */
	@AliasFor("value")
	String name() default "";

	/**
	 * 是否需要path变量。
         * 默认为true,如果传入请求中缺少path变量,则会引发异常。
	 */
	boolean required() default true;

}

【2】对于注解@PahVariable默认入参设置,使用较多的是value(),有时候也可以省略value,直接设置入参别名。举个栗子:

@GetMapping("/userInfo3/{id}")
    public ResponseMessage select3(@PathVariable String id) {
        User user = new User();
        user.setId(id);
        user.setRemark("select3()这个方法使用注解@PathVariable接收单参数");
        return new ResponseMessage(ResponseStatus.SUCCESS.getStatus(),
            ResponseStatus.SUCCESS.getMessage(), user);
}
  • postman测试结果: 

  • 注意:
  • 如果想要url 中占位符中的 id 值直接赋值到参数id中,需要保证url 中的参数和方法接收参数一致,否则就无法接收。

【3】如果不一致的话,也可以解决,需要用 @PathVariable` 中的 value 属性来指定对应关系。举个栗子:

@GetMapping("/userInfo4/{userId}")
    public ResponseMessage select4(@PathVariable(value = "userId") String id) {
        User user = new User();
        user.setId(id);
        user.setRemark("select4()这个方法使用注解@PathVariable接收参数");
        return new ResponseMessage(ResponseStatus.SUCCESS.getStatus(), 
            ResponseStatus.SUCCESS.getMessage(), user);
}
  • postman测试结果:  

 

【4】对于访问的 url,占位符的位置可以在任何位置,不一定非要在最后。

例如:/xxx/{id}/user。

另外,url 也支持多个占位符,方法参数使用同样数量的参数来接收,原理和一个参数是一样的。

举个栗子:

@GetMapping("/userInfo5/{userId}/{userName}")
public ResponseMessage select5(@PathVariable(value = "userId") String id, @PathVariable String userName) {
        User user = new User();
        user.setId(id);
        user.setUserName(userName);
        user.setRemark("select5()这个方法使用注解@PathVariable接收参数");
        return new ResponseMessage(ResponseStatus.SUCCESS.getStatus(),
            ResponseStatus.SUCCESS.getMessage(), user);
}
  • postman测试结果:  

所以支持多个参数的接收。同样地,如果 url 中的参数和方法中的参数名称不同的话,也需要使用 value 属性来绑定两个参数。


2.4 @RequestParam

【1】注解@RequestParam是获取请求参数的,前边的栗子可以知道@PathValiable注解也是获取请求参数的。

  • @RequestParam和@PathVariable有什么区别呢?
  •  
  • @PathValiable是从 url 模板中获取参数值, 即这种风格的 url:http://localhost:8080/user/{id}
  • @RequestParam是从 request 里面获取参数值,即这种风格的 url:http://localhost:8080/user?id=1
  • 注解@RequestParam的源码定义:
/**
 *该注释表示方法参数应该绑定到web请求参数。
 *
 * 支持Spring MVC和Spring WebFlux中的带注释的处理程序方法如下:
 * 在Spring MVC中,“请求参数”映射到多部分请求中的查询参数、表单数据和部件。
 * 这是因为Servlet API将查询参数和表单数据组合成一个称为“parameters”的映射,其中包括对请求体的自动解析。
 * 在Spring WebFlux中,“请求参数”只映射到查询参数。
 * 要处理所有3个部分(查询、表单数据和多部分数据),您可以使用数据绑定到一个带有{@link ModelAttribute}注释的命令对象。
 * 如果方法参数类型是{@link Map},并且指定了一个请求参数名,
 * 那么假设有合适的转换策略,请求参数值将转换为{@link Map}。
 * 如果方法参数是{@link java.util。Map Map<String, String>}
 * 或{@link org.springframework.util}
 * 如果没有指定参数名,则使用所有请求参数名和值填充映射参数。
 */
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {

	/**
	 * name的别名
	 */
	@AliasFor("name")
	String value() default "";

	/**
	 * 要绑定到的请求参数的名称。
	 */
	@AliasFor("value")
	String name() default "";

	/**
	 * 是否需要参数。
         * 默认为{@code true},如果请求中缺少参数,则会引发异常。
         * 如果您希望在请求中没有参数的情况下使用{@code null}值,则将该值转换为{@code false}。
         * 或者,提供一个{@link #defaultValue},它隐式地将这个标志设置为{@code false}。
	 */
	boolean required() default true;

	/**
	 * 在没有提供请求参数或请求参数为空值时用作回退的默认值。
         * 提供默认值隐式地将{@link #required}设置为{@code false}。
	 */
	String defaultValue() default ValueConstants.DEFAULT_NONE;

}
  • 举个栗子:
@GetMapping("/userInfo6")
public ResponseMessage select6(@RequestParam String id){
        User user = new User();
        user.setId(id);
        user.setRemark("select6()这个方法使用注解@RequestParam接收参数");
        return new ResponseMessage(ResponseStatus.SUCCESS.getStatus(),
            ResponseStatus.SUCCESS.getMessage(), user);
}
  • postman测试结果:

  

看上边postman测试结果,可以正常从控制台打印出 id 信息。

注意:

上边url中携带的参数和select6()方法中接收的参数需要保持一致。

【2】如果不一致,也需要使用value属性来说明,比如 url 为:http://localhost:8080/user?userId=ABCD

  • 举个栗子:
@RequestMapping("/userInfo7")
public ResponseMessage select7(@RequestParam(value = "userId", required = false) String id) {
        User user = new User();
        user.setId(id);
        user.setRemark("select7()这个方法使用注解@RequestParam接收参数");
        return new ResponseMessage(ResponseStatus.SUCCESS.getStatus(),
            ResponseStatus.SUCCESS.getMessage(), user);
}
  • postman测试结果:

 

除了 value 属性外,还有个两个属性比较常用:

  • required 属性:true 表示该参数必须要传,否则就会报 404 错误,false 表示可有可无。
  • defaultValue 属性:默认值,表示如果请求中没有同名参数时的默认值。

从url请求地址中可以看出,注解@RequestParam用于GET请求时,接收拼接在url中的参数。

【3】除此之外,该注解还可以用于 POST 请求,接收前端表单提交的参数,假如前端通过表单提交 userName 和 password 两个参数,那我们可以使用@RequestParam来接收,用法和上面一样。

  • 举个栗子:
@PostMapping("/userInfo8")
public ResponseMessage select8(@RequestParam String userName, @RequestParam String password) {
        User user = new User();
        user.setUserName(userName);
        user.setPassword(password);
        user.setRemark("select8()这个方法使用注解@RequestParam接收参数");
        return new ResponseMessage(ResponseStatus.SUCCESS.getStatus(),
            ResponseStatus.SUCCESS.getMessage(), user);
}
  • postman测试结果:

注意:提交多个参数时,url中入参用&&符号连接。

 【4】如果表单数据很多,我们不可能在后台方法中写上很多参数,每个参数还要@RequestParam注解。针对这种情况,需要封装一个实体类来接收这些参数,实体中的属性名和表单中的参数名一致即可。

  • 实体类User.java:
public class User {

    private String id;

    private String userName;

    private String password;

    private Date birthday;

    private String remark;
    
    /**省去构造方法、setter和getter方法*/
}
  • 使用实体接收的话,不能在前面加@RequestParam注解了,直接使用即可。 
  • 举个栗子:
@PostMapping("/userInfo9")
public ResponseMessage select9(User user) {
        User user1 = new User();
        user1.setId(user.getId());
        user1.setUserName(user.getUserName());
        user1.setPassword(user.getPassword());
        user1.setBirthday(new Date());
        user1.setRemark("select9()这个方法使用注解@RequestParam接收参数");
        return new ResponseMessage(ResponseStatus.SUCCESS.getStatus(), 
            ResponseStatus.SUCCESS.getMessage(), user1);
}
  • postman测试结果:

在postman中测试一下表单提交,可以看到url访问地址携带的路径参数最后传递方法中的实体类对象。在实际项目开发中,表单数据一般都很多,这时多考虑封装一个实体类来接收表单数据即可。


2.5 @RequestBody

@RequestBody注解用于接收前端传来的实体,接收参数也是对应的实体。例如,前端通过JSON提交传来两个参数 userName和 password,此时我们需要在后端封装一个实体对象来接收。当前端传递的参数比较多时,使用@RequestBody接收会非常方便。

  • 【1】注解@RequestBody源码定义:
/**
 * 表示方法参数的注释应该绑定到web请求的主体。
 * 请求体通过一个{@link HttpMessageConverter}来解析方法参数,该参数取决于请求的内容类型。
 * 另外,可以使用{@code @Valid}注释参数来应用自动验证。
 * 支持带注释的处理程序方法。
 */
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestBody {

	/**
	 * 是否需要正文内容。
         * 默认值是{@code true},在没有正文内容的情况下抛出异常。
         * 如果您希望在正文内容为{@code null}时传递{@code null},请将此转换为{@code false}。
	 */
	boolean required() default true;
}
  • 举个栗子:
@PostMapping("/userInfo10")
public ResponseMessage select10(@RequestBody User user) {
    user.setBirthday(new Date());
    user.setRemark("select10()这个方法使用注解@RequestParam接收参数");
    return new ResponseMessage(ResponseStatus.SUCCESS.getStatus(), 
        ResponseStatus.SUCCESS.getMessage(), user);
}
  • postman测试结果:

根据postman测试结果,可知注解@RequestBody用于 POST 请求上,接收 json 实体参数。它和上面我们介绍的表单提交有点类似,只是参数的格式不同,一个是JSON实体,一个是表单提交。在实际项目中根据具体场景和需要使用对应的注解即可。


3、总结

本节内容主要总结分了Spring Boot中对 MVC的支持,分析了两类注解的特点、场景使用、源码分析:

  • 【1】组件型注解

@Component:没有明确的角色定,在类定义之前添加@Component注解,它会被spring容器识别,并转为bean

@Repository:用于数据访问层(DAO层)的注解 (特殊的@Component)

@Service:用于对业务逻辑层的注解, (特殊的@Component)

@Controller:用于表现层(MVC-->SpringMVC)注解 , (特殊的@Component)

  • 【2】请求和参数型注解

主要总结@RestController、@RequestMapping、@PathVariable、@RequestParam和@RequestBody等注解的特点,场景使用,使用方式及源码分析。由于@RestController注解集成@Controller和@ResponseBody,所以本文对返回 json 的注解不再赘述。以上两种类型包含的注解均是使用频率较高很高的注解,在实地项目开发中基本都会遇到,总结于此。

测试Demo:【4-SpringBoot-mvc】:SpringBoot中对MVC的支持

测试Demo的源码:https://github.com/JohnnyHL/SpringBoot-Item.git


愿你就像早晨八九点钟的太阳,活力十足,永远年轻。

原创文章 104 获赞 154 访问量 13万+

猜你喜欢

转载自blog.csdn.net/qq_27706119/article/details/105467520
今日推荐