SpringMVC中Controller层常用注解

1.创建一个名叫springboot-controller-annotation的SpringBoot项目,并选择DevTools、Lombok和Spring Web三个依赖。

提示:如果提示lnitialization failed for 'https://start.spring.io'Please check URL, network and proxy settings.错误,可以点击左上角File,选择Setting,搜索HTTP Proxy,点击Auto-detect proxy settins,然后选择Automatic proxy configuration URL输入https://start.spring.io,点击OK,再次进行创建SpringBoot项目。

2.设置本地Maven。然后点击右侧Maven,然后点击重新加载图标。

3.在com.example.annotation包下创建包controller,并创建MyController类。

4.@Controller注解:只可用在类上,和@Component功能一样,把添加这个注解的类注册到容器,可以传字符串当做注册到容器中的类的名字。只是为了更加明确语义,才用@Controller注解加到MVC架构的Controller层。当@Controller不传值时,默认将类名第一个字母变为小写,其他不变,传入容器。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}

例子1:

@Controller("newController")
public class MyController {
}

例子2:

@Controller
public class MyController {
}

结果:

package com.example.annotation;

import com.example.annotation.controller.MyController;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class SpringbootControllerAnnotationApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(SpringbootControllerAnnotationApplication.class, args);
        String[] beanNamesForType = run.getBeanNamesForType(MyController.class);
        for (String s : beanNamesForType) {
            System.out.println(s);
        }
    }

}

通过按类型查看注入容器中MyController类的名字,查看控制台打印。

结果1:

结果2:

5.@RestController注解:只可用在类上,表示返回值是数据,而不是映射地址或者页面,并将类注入容器。@RestController注解等价于@Controller和@ResponseBody两者组成的共同注解。@RestController注解和@Controller注解一样,可以传字符串参数当做类注册到容器中的名字,不传值时,默认把类名第一个字母变为小写后,传入容器。导入容器的名字可以自己测试。其他内容,下面会进行测试。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
    @AliasFor(annotation = Controller.class)
    String value() default "";

}

6.@RequestMapping注解:可以用到类或者方法上,用来表示映射地址和请求类型等。参数有name(String)、value(String [])、path(String [])、method(RequestMethod [])、params(String [])、

headers(String [])、consumes(String [])、produces(String [])共8个。最为常用的就是value和method这两个属性。value用来设置映射的值,也就是请求这个映射地址时,调用对应的方法。method用于设置请求方式,常用的请求方式有RequestMethod.POSTRequestMethod.DELETERequestMethod.PUTRequestMethod.GET四种,分别用于增删改查。直接给@RequestMapping传值,等于给@RequestMapping中的value属性传值。也就是@RequestMapping("/test")等价于@RequestMapping(value = "/test")。但是@RequestMapping用在类上一般只传value值,value属性可以不用写,也就是直接写

@RequestMapping(映射地址)即可。一旦给类上加上映射地址,访问方法时,需要都加上这个地址,然后再加上方法上面的映射地址,才能调用本方法。

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

	String name() default "";

	@AliasFor("path")
	String[] value() default {};

	@AliasFor("value")
	String[] path() default {};

	RequestMethod[] method() default {};

	String[] params() default {};

	String[] headers() default {};

	String[] consumes() default {};

	String[] produces() default {};

}

由于@RequestMapping有两个属性较为常用,但是默认传值只能赋值给一个属性。所以,针对四个请求方式分别延伸出了@PostMapping、@DeleteMapping、@PutMapping和@GetMapping四个注解。这样,注解可以表明请求方式,默认传值可以表示映射地址。以get请求为例,@RequestMapping(value = "/test",method = RequestMethod.GET)等价于@GetMapping(value = "/test")等价于@GetMapping("/test")。但是延伸出来的四个注解只能用在方法上,不能用在类上了。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.GET)
public @interface GetMapping {

	@AliasFor(annotation = RequestMapping.class)
	String name() default "";

	@AliasFor(annotation = RequestMapping.class)
	String[] value() default {};

	@AliasFor(annotation = RequestMapping.class)
	String[] path() default {};

	@AliasFor(annotation = RequestMapping.class)
	String[] params() default {};

	@AliasFor(annotation = RequestMapping.class)
	String[] headers() default {};

	@AliasFor(annotation = RequestMapping.class)
	String[] consumes() default {};

	@AliasFor(annotation = RequestMapping.class)
	String[] produces() default {};

}

例子1:

package com.example.annotation.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyController {

    @GetMapping("/test1")
    public String test1() {
        return "test1";
    }
    @RequestMapping(value = "/test2",method = RequestMethod.GET)
    public String test2() {
        return "test2";
    }
}

结果1:浏览器中访问http://localhost:8080/test1http://localhost:8080/test2分别可以得到以下结果。

例子2:

package com.example.annotation.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user")
public class MyController {

    @GetMapping("/test1")
    public String test1() {
        return "test1";
    }
    @RequestMapping(value = "/test2",method = RequestMethod.GET)
    public String test2() {
        return "test2";
    }
}

结果2:由于在类上加上了映射地址,再请求结果1中的两个地址会报404错误。需要通过类映射地址+方法地址的方式,调用方法。

浏览中访问http://localhost:8080/test1http://localhost:8080/test2http://localhost:8080/user/test1http://localhost:8080/user/test2的结果依次如下。

 

7.@ResponseBody:可以用在类或方法上,表示返回值是数据,而不是映射地址或者页面。

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

}

为了测试@ResponseBody功能,在pom.xml的<dependencies></dependencies>导入thymeleaf依赖。点击右上角m加旋转标志,重新加载依赖。

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

在src/resources/templates文件夹下创建test.html文件。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <input type="text">
    <h3>这是用来测试的</h3>
</body>
</html>

例子1:

package com.example.annotation.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class MyController {

    @GetMapping("/test")
    public String test1() {
        return "test";
    }
}

结果1:当不加@ResponseBody时,会自动将字符串解析成页面。浏览器访问http://localhost:8080/test,发现进入了test.html的页面。

例子2:

package com.example.annotation.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@ResponseBody
public class MyController {

    @GetMapping("/test")
    public String test1() {
        return "test";
    }
}

结果2:加上了@ResponseBody注解,浏览器访问http://localhost:8080/test,发现返回的是test字符串。@ResponseBody注解加在类上,表示类下面的所有方法返回值是数据,而不是映射地址或者页面。@ResponseBody注解加在方法上,表示本方法返回值是数据,而不是映射地址或者页面。

例子3:

package com.example.annotation.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyController {

    @GetMapping("/test")
    public String test1() {
        return "test";
    }
}

结果3:本方法为了测试序号5中,@RestController注解等价于@Controller和@ResponseBody两者组成的共同注解。现在前后端分离较为流行,一般直接将Controller层的所有类上都上@RestController注解,让Controller层只操作数据,不操作页面。浏览器访问http://localhost:8080/test,发现产生和结果2一样的结果。

8.为了方便下面注解的测试,先在src/resources/templates文件夹下创建test2.html文件。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="http://cdn.bootcss.com/jquery/1.12.2/jquery.js"></script>
    <script>
        function bodySubmit(number) {
            let id = "1";
            let name = "bodyTest";
            let url = "/test/bodyTwo";
            if(number === 3) {
                url = "/test/bodyThree"
            }
            let data = {
                    "id": id,
                    "name": name
            };
            $.ajax({
                type: "post",
                url: url,
                dataType: "json",
                contentType: "application/json;charset=utf-8",
                data: JSON.stringify(data),
                success: function (res) {
                    console.log(res);
                }
            })
        }
    </script>
</head>

<body>
    <a href="/test/param?name=paramTest1&id=1">@RequestParam测试1</a><br><br>
    <a href="/test/path/1/test/pathTest">@PathVariable测试1</a><br><br>
    <a href="/test/toAttribute">@RequestAttribute测试1</a><br><br>
    <form action="/test/bodyOne" method="post">
        <input type="text" name="id" value="1" hidden>
        <input type="text" name="name" value="bodyType" hidden>
        <input type="submit" value="@RequestBody测试1">
    </form><br>
    <button onclick="bodySubmit(2)">@RequestBody测试2</button><br><br>
    <button onclick="bodySubmit(3)">@RequestBody测试3</button><br><br>
    <a href="/test/header">@RequestHeader测试1</a><br><br>
    <a href="/test/cookie">@CookieValue测试1</a><br><br>
    <a href="/test/matrix;id=1;name=matrixTest1/matrix/matrixTwo;idTwo=2/matrixThree;id=3,4;name=name3,name4">@MatrixVariable测试1</a>
    

</body>

</html>

9.@RequestParam注解:只能用在方法的参数上,表示从请求参数上获取值,并赋值给所修饰的方法参数。此注解常与GET请求结合使用。@RequestParam一共有name(String),value(String),required(boolean),defaultValue(String)四个参数。常用的只有value,required,defaultValue三个参数。value表示请求参数的名称,@RequestParam不声明具体参数的传值,默认给value传值。required表示此参数是否必填,默认true。当required为true时,表示请求参数中必须有这个参数,否则会报错。defaultValue表示当请求参数上没有这个参数时,会给个默认值。

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {

	@AliasFor("name")
	String value() default "";

	@AliasFor("value")
	String name() default "";

	boolean required() default true;

	String defaultValue() default ValueConstants.DEFAULT_NONE;

}

例子:

package com.example.annotation.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

@Controller
@RequestMapping("/test")
public class MyController {

    @RequestMapping("")
    public String test() {
        return "test2";
    }

    @GetMapping("/param")
    @ResponseBody
    public  Map<String, String> paramTest1(@RequestParam String name,
                                           @RequestParam("id") String id,
                                           @RequestParam(value = "id") String getId,
                                           @RequestParam(defaultValue = "15") String age,
                                           @RequestParam(required = false) String hobby,
                                           @RequestParam(required = false,defaultValue = "空地址") String address,
                                           @RequestParam(value = "sex",required = false,defaultValue = "男") String sex,
                                           @RequestParam Map<String,String> paramMap
                                            ) {
        Map<String, String> map = new HashMap<>();
        map.put("name",name);
        map.put("id",id);
        map.put("getId",getId);
        map.put("age",age);
        map.put("hobby",hobby);
        map.put("address",address);
        map.put("sex",sex);
        map.put("paramMap",paramMap.toString());
        return map;
    }
}

结果:浏览器访问http://localhost:8080/test,点击“@RequestParam测试1”,会产生以下结果。(请求地址是http://localhost:8080/test/param?name=paramTest1&id=1

总结(以下名称均是方法参数的名称):

1.从getId参数可以发现,方法参数名称和请求参数名称可以不相同,只要保证注解的value值与请求参数名称相同即可。

2.从id和getId参数可以发现,给注解传值,不指定参数名称时,会自动给value参数赋值。

3.从name和id参数可以发现,不给注解赋值时,会将方法参数名称和请求参数名称进行绑定。

4.从age参数可以发现,方法参数从请求参数中没有找到绑定值,且定义了defaultValue时,会取出defaultValue值赋值给方法参数。

5.从hobby参数可以发现,当required值为false时,哪怕请求参数中没有方法参数需要的值时,也不会报错。

6.address参数的注解表示的意思是方法参数绑定名称为address的请求参数,并且允许此请求参数不存在,方法参数的默认值为“空地址”。

7.sex参数的注解表示的意思是方法参数绑定名称为sex的请求参数,并且允许此请求参数不存在,方法参数的默认值为“男”。

8.从paramMap参数可以发现,可以将所有的请求参数通过类型为Map<String,String>的map进行接收。

9.如果required的值为true,且没有给defaultValue赋值时,并且方法参数没有在请求参数值中找到绑定值时,会报错。

10.@PathVariable注解:只能用在方法的参数上,表示从请求路径上获取值,并赋值给所修饰的方法参数。@PathVariable一共有name(String),value(String),required(boolean)三个参数。常用的只有value,required两个参数。value表示请求地址中参数的名称,@PathVariable不声明具体参数的传值,默认给value传值。required表示此参数是否必填,默认true。当required为true时,表示请求地址的参数中必须有这个参数,否则会报错。

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PathVariable {

	@AliasFor("name")
	String value() default "";

	@AliasFor("value")
	String name() default "";

	boolean required() default true;

}

例子:

package com.example.annotation.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

@Controller
@RequestMapping("/test")
public class MyController {

    @RequestMapping("")
    public String test() {
        return "test2";
    }

    @GetMapping("/param")
    @ResponseBody
    public Map<String, String> paramTest1(@RequestParam String name,
                                          @RequestParam("id") String id,
                                          @RequestParam(value = "id") String getId,
                                          @RequestParam(defaultValue = "15") String age,
                                          @RequestParam(required = false) String hobby,
                                          @RequestParam(required = false, defaultValue = "空地址") String address,
                                          @RequestParam(value = "sex", required = false, defaultValue = "男") String sex,
                                          @RequestParam Map<String, String> paramMap
    ) {
        Map<String, String> map = new HashMap<>();
        map.put("name", name);
        map.put("id", id);
        map.put("getId", getId);
        map.put("age", age);
        map.put("hobby", hobby);
        map.put("address", address);
        map.put("sex", sex);
        map.put("paramMap", paramMap.toString());
        return map;
    }

    @GetMapping("/path/{id}/test/{name}")
    @ResponseBody
    public Map<String, String> pathTest1(@PathVariable String id,
                                         @PathVariable("name") String name,
                                         @PathVariable(value = "name") String getName,
                                         @PathVariable(required = false) String age,
                                         @PathVariable(value = "address", required = false) String address,
                                         @PathVariable Map<String, String> pathMap
    ) {
        Map<String, String> map = new HashMap<>();
        map.put("id", id);
        map.put("name", name);
        map.put("getName", getName);
        map.put("age", age);
        map.put("address", address);
        map.put("pathMap", pathMap.toString());
        return map;
    }

结果:浏览器访问http://localhost:8080/test,点击“@PathVariable测试1”,会产生以下结果。(请求地址是http://localhost:8080/test/path/1/test/pathTest

总结(以下名称均是方法参数的名称):

1.从name和getName参数可以发现,给注解传值,不指定参数名称时,会自动给value参数赋值。

2.从id和getName参数可以发现,不给注解赋值时,会将方法参数名称和请求地址的参数名称进行绑定。

3.从age参数可以发现,当required值为false时,哪怕请求地址的参数中没有方法参数需要的值时,也不会报错。

4.address参数的注解表示的意思是方法参数绑定名称为address的请求地址的参数,并且允许此请求地址的参数不存在。

5.从pathMap参数可以发现,可以将所有请求地址的参数通过类型为Map<String,String>的map进行接收。

6.如果required的值为true,并且方法参数没有在请求地址的参数值中找到绑定值时,会报错。

11.@RequestAttribute注解:只能用在方法的参数上,表示从Attribute上获取值,并赋值给所修饰的方法参数。@RequestAttribute一共有name(String),value(String),required(boolean)三个参数。常用的只有value,required两个参数。value表示Attribute中参数的名称,@RequestAttribute不声明具体参数的传值,默认给value传值。required表示此参数是否必填,默认true。当required为true时,表示Attribute中必须有这个参数,否则会报错。

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestAttribute {

	@AliasFor("name")
	String value() default "";

	@AliasFor("value")
	String name() default "";

	boolean required() default true;

}

例子:

package com.example.annotation.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

@Controller
@RequestMapping("/test")
public class MyController {

    @RequestMapping("")
    public String test() {
        return "test2";
    }

    @GetMapping("/toAttribute")
    public String toAttribute(Model model) {
        model.addAttribute("id","1");
        model.addAttribute("name","attributeTest");
        return "forward:attribute";
    }

    @GetMapping("/attribute")
    @ResponseBody
    public Map<String,String> attributeTest(@RequestAttribute String id,
                                            @RequestAttribute("name") String name,
                                            @RequestAttribute(value = "name") String getName,
                                            @RequestAttribute(required = false) String age,
                                            @RequestAttribute(value = "address", required = false) String address
                                            ) {
        Map<String,String> map = new HashMap<>();
        map.put("id", id);
        map.put("name", name);
        map.put("getName", getName);
        map.put("age", age);
        map.put("address", address);
        return map;
    }
}

结果:浏览器访问http://localhost:8080/test,点击“@RequestAttribute测试1”,会产生以下结果。(请求地址是http://localhost:8080/test/toAttribute

总结(以下名称均是方法参数的名称):

1.从name和getName参数可以发现,给注解传值,不指定参数名称时,会自动给value参数赋值。

2.从id和getName参数可以发现,不给注解赋值时,会将方法参数名称和Attribute的参数名称进行绑定。

3.从age参数可以发现,当required值为false时,哪怕Attribute的参数中没有方法参数需要的值时,也不会报错。

4.address参数的注解表示的意思是方法参数绑定名称为address的Attribute的参数,并且允许此Attribute的参数不存在。

5.如果required的值为true,并且方法参数没有在Attribute的参数值中找到绑定值时,会报错。

12.@RequestBody注解:只能用在方法参数上,表示从JSON字符串上获取值,并赋值给所修饰的方法参数。此注解常与POST请求结合使用,所修饰的方法参数常常是Map或者pojo类。并且,一个请求中只能有一个@RequestBody注解。@RequestBody注解只有一个required(boolean)参数。required表示此参数是否必填,默认为true。当required为true时,没有请求体传过来,会报错。

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestBody {

	boolean required() default true;

}

例子1:

package com.example.annotation.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

@Controller
@RequestMapping("/test")
public class MyController {

    @RequestMapping("")
    public String test() {
        return "test2";
    }

    @PostMapping("/bodyOne")
    @ResponseBody
    public String bodyTest1(@RequestBody String content ) {
        return content;
    }

}

结果1:浏览器访问http://localhost:8080/test,点击“@RequestBody测试1”,会产生以下结果。由于http提交form表单默认请求头是application/x-www-form-urlencoded,而不是JSON字符串。所以,此类型的表单只能用String类型数据进行接收,用Map或pojo类进行接收会报错。如果需要数据,自行处理字符串。这样,失去了用@RequestBody的初衷,还不如用@RequestParam更加方便。

例子2:

package com.example.annotation.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

@Controller
@RequestMapping("/test")
public class MyController {

    @RequestMapping("")
    public String test() {
        return "test2";
    }

    @PostMapping("/bodyTwo")
    @ResponseBody
    public Map<String,Object> bodyTest2(@RequestBody Map<String,Object> map ) {
        System.out.println(map);
        return map;
    }
}

结果2:前端用ajax传递post请求,设置请求头和数据类型为JSON,用JSON.stringify将数据封装为JSON数据,三者缺一不可,否则会报错(具体可以查看序号8中test2.html文件)。可以通过设置类型为 Map<String,Object>的map接收数据,这是一种万能接收数据的方式。后期,可以根据自己需要,将Object数据转换为自己需要的数据。如果都是一种类型的数据,也可以自行设置接收类型。

浏览器访问http://localhost:8080/test,点击“@RequestBody测试2”,会产生以下结果。

例子3:

package com.example.annotation.controller;

import com.example.annotation.pojo.Person;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

@Controller
@RequestMapping("/test")
public class MyController {

    @RequestMapping("")
    public String test() {
        return "test2";
    }

    @PostMapping("/bodyThree")
    @ResponseBody
    public String bodyTest3(@RequestBody Person person) {
        System.out.println(person);
        return person.toString();
    }
}

结果3:前端和结果2中一样,后端用Person类接收数据,会自动将JSON字符串中的值和Person类的属性进行绑定。这样对数据较多的类来说,操作数据更加方便。

浏览器访问http://localhost:8080/test,点击“@RequestBody测试3”,会产生以下结果

补充:在src/main/java的com.example.annotation包下面创建名叫pojo的包,并在此包下创建Person类。

package com.example.annotation.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Person {

    private String id;

    private String name;

}

13.@RequestHeader注解:只能用在方法的参数上,表示从Header中获取数据,并赋值给所修饰的方法参数。@RequestHeader一共有name(String),value(String),required(boolean),defaultValue(String)四个参数。常用的只有value,required,defaultValue三个参数。value表示RequestHeader参数的名称,@RequestHeader不声明具体参数的传值,默认给value传值。required表示此参数是否必填,默认true。当required为true时,表示RequestHeader参数中必须有这个参数,否则会报错。defaultValue表示当RequestHeader参数上没有这个参数时,会给个默认值。

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestHeader {

	@AliasFor("name")
	String value() default "";

	@AliasFor("value")
	String name() default "";

	boolean required() default true;

	String defaultValue() default ValueConstants.DEFAULT_NONE;

}

@RequestHeader注解只可以Request Headers获取的参数,不可以获取Response Headers的参数。

例子:

package com.example.annotation.controller;

import com.example.annotation.pojo.Person;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

@Controller
@RequestMapping("/test")
public class MyController {

    @RequestMapping("")
    public String test() {
        return "test2";
    }

    @GetMapping("/header")
    @ResponseBody
    public Map<String,String> headerTest(@RequestHeader String Connection,
                                         @RequestHeader("Host") String host,
                                         @RequestHeader(value = "Host") String getHost,
                                         @RequestHeader(required = false) String name,
                                         @RequestHeader(value = "address", required = false) String address,
                                         @RequestHeader Map<String, String> headerMap
                                         ) {
        Map<String,String> map = new HashMap<>();
        map.put("host",host);
        map.put("Connection",Connection);
        map.put("getHost",getHost);
        map.put("name",name);
        map.put("address",address);
        map.put("headerMap",headerMap.toString());
        return map;
    }
}

结果:浏览器访问http://localhost:8080/test,点击“@RequestHeader测试1”,会产生以下结果。

总结:可以参考序号10中@PathVariable注解,@PathVariable注解的6条总结,同样适用这个注解。

写例子时,遇到了两个小事情,同样总结下来,就是@RequestHeader只能用于Get请求,否则会报错。我是为了简化代码,复制上面的代码才导致出现这个问题,一般获取数据都是用@GetMapping,只要写代码规范就不会遇到。

另一个就是获取Request Headers没有的参数时,IDEA会划横线,这样有效避免了写错参数名,比如上面的address,这是为了测试才写的,不过也不属于程序错误。

14.@CookieValue:只能用于方法的参数上,表示从Cookie中获取数据,并赋值给所修饰的方法参数。@CookieValue一共有name(String),value(String),required(boolean),defaultValue(String)四个参数。常用的只有value,required,defaultValue三个参数。value表示Cookie中参数的名称,@CookieValue不声明具体参数的传值,默认给value传值。required表示此参数是否必填,默认true。当required为true时,表示Cookie的参数中必须有这个参数,否则会报错。defaultValue表示当Cookie的参数上没有这个参数时,会给个默认值。

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CookieValue {

	@AliasFor("name")
	String value() default "";

	@AliasFor("value")
	String name() default "";

	boolean required() default true;

	String defaultValue() default ValueConstants.DEFAULT_NONE;

}

例子:

package com.example.annotation.controller;

import com.example.annotation.pojo.Person;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

@Controller
@RequestMapping("/test")
public class MyController {

    @RequestMapping("")
    public String test() {
        return "test2";
    }


	@GetMapping("/cookie")
    @ResponseBody
    public Map<String,String> cookieTest(@CookieValue String id,
                                   @CookieValue("name") String name,
                                   @CookieValue(value = "name") String getName,
                                   @CookieValue(required = false) String age,
                                   @CookieValue(value = "address", required = false) String address) {
        Map<String,String> map = new HashMap<>();
        map.put("id",id);
        map.put("name",name);
        map.put("getName",getName);
        map.put("age",age);
        map.put("address",address);
        return map;
    }
}

结果:浏览器访问http://localhost:8080/test,点击“@CookieValue测试1”,会产生以下结果。

补充:手动添加cookie到http://localhost:8080中。浏览访问http://localhost:8080/test(最好使用谷歌浏览器,这样会和我统一,设置看起来差不多),点击鼠标右键,选择最下面的“检查”,选择“Application”,选择左侧Cookies点击http://localhost:8080网址下,然后添加id和name属性的值。

总结:可以参考序号11中@RequestAttribute注解,@RequestAttribute注解的5条总结,同样适用这个注解。

15.@MatrixVariable注解:只能用于方法的参数上,表示从路径中获取数据,并赋值给所修饰的方法参数。感觉起来和@PathVariable差不多,都是从路径上获取值。但是,两者差距很大的,@PathVariable注解,无论是变量还是路径之间都是用"/"隔开,且变量用"{}"包裹。而@MatrixVariable注解,路径之间还是用"/"隔开,但是变量之间或者变量与路径之间以分号";"隔开,并且后跟变量的路径上加"{}"。@PathVariable一共有name(String),value(String),pathVar(String),required(boolean),defaultValue(String)五个参数。常用的只有value,pathVar,required,defaultValue四个参数。value表示路径中参数的名称,@MatrixVariable不声明具体参数的传值,默认给value传值。required表示此参数是否必填,默认true。当required为true时,表示路径的参数中必须有这个参数,否则会报错。defaultValue表示当路径的参数上没有这个参数时,会给个默认值。对于第一次出现的pathVar属性,表示的是路径名称,由于这种特有的矩阵传参方式,参数名是跟在路径后面的,为了区分不同路径下同名的参数,才有了这个属性。

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MatrixVariable {

	@AliasFor("name")
	String value() default "";

	@AliasFor("value")
	String name() default "";

	String pathVar() default ValueConstants.DEFAULT_NONE;

	boolean required() default true;

	String defaultValue() default ValueConstants.DEFAULT_NONE;

}

注意:SpringBoot默认禁用了矩阵变量的功能,如果想要使用,需要手动配置。查看WebMvcAutoConfiguration类下configurePathMatch()方法,发现创建了UrlPathHelper类。点击这个类进去查看源码,发现UrlPathHelper有个属性removeSemicolonContent的值为true(removeSemicolonContent表示是否移除分号功能)。查看SpringBoot官网,我们可以发现有两种方式解决这个问题。第一种方式就是在配置类中注入WebMvcConfigurer接口的实现类 ,第二种方式就是重写configurePathMatch()方法。

在src/main/java的com.example.annotation下创建一个名叫config的包,并在此包下面创建一个名叫MyWebConfig的类。

package com.example.annotation.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.util.UrlPathHelper;

@Configuration
public class MyWebConfig {
    @Bean
    public WebMvcConfigurer webMvcConfigurer() {
        return  new WebMvcConfigurer() {
            @Override
            public void configurePathMatch(PathMatchConfigurer configurer) {
                UrlPathHelper urlPathHelper = new UrlPathHelper();
                urlPathHelper.setRemoveSemicolonContent(false);
                configurer.setUrlPathHelper(urlPathHelper);
            }
        };
    }
}
package com.example.annotation.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.util.UrlPathHelper;

@Configuration
public class MyWebConfig implements WebMvcConfigurer {

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        UrlPathHelper urlPathHelper = new UrlPathHelper();
        urlPathHelper.setRemoveSemicolonContent(false);
        configurer.setUrlPathHelper(urlPathHelper);
    }
}

例子:

package com.example.annotation.controller;

import com.example.annotation.pojo.Person;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Controller
@RequestMapping("/test")
public class MyController {

    @RequestMapping("")
    public String test() {
        return "test2";
    }

    @GetMapping("/{matrix}/matrix/{matrixTwo}/{matrixThree}")
    @ResponseBody
    public Map<String,String> matrixTest(@MatrixVariable String idTwo,
                                         @MatrixVariable("idTwo") String id2,
                                         @MatrixVariable(pathVar="matrix",value = "id") String id1,
                                         @MatrixVariable(pathVar = "matrixThree",value = "id") List<String> id3,
                                         @MatrixVariable(pathVar = "matrixFour",value = "id",defaultValue = "4",required = false) String id4,
                                         @MatrixVariable Map<String,String> matrixMap1,
                                         @MatrixVariable(pathVar = "matrixTwo") Map<String,String> matrixMap2
                                         ) {
        Map<String,String> map = new HashMap<>();
        map.put("idTwo",idTwo);
        map.put("id2",id2);
        map.put("id1",id1);
        map.put("id3",id3.toString());
        map.put("id4",id4);
        map.put("name3",matrixMap1.toString());
        map.put("matrixMap",matrixMap2.toString());
        return map;
    }
}

结果:浏览器访问http://localhost:8080/test,点击“@MatrixVariable测试1”,会产生以下结果。(url为http://localhost:8080/test/matrix;id=1;name=matrixTest1/matrix/matrixTwo;id=2/matrixThree;id=3,4;name=name3,name4

总结(以下名称均是方法参数的名称):

1.从idTwo参数可以发现,不给注解传任何值时,默认就是在路径上找名叫idTwo参数,但是要保证这个名称的在路径上唯一,否则会报错。

2.从idTwo和id2参数可以发现,给注解传值,给注解传值,不指定参数名称时,会自动给value参数赋值。

3.从id1参数可以发现,通过路径名称和参数名称可以精确获取值。

4.从id3可以发现,当一个路径参数赋多个值时,被注解修饰的方法参数可以通过List接收。

4.从id4参数可以发现,当required值为false时,哪怕路径的参数中没有方法参数需要的值时,也不会报错。

5.从matrixMap1可以发现,可以获取某个路径上所有的参数,并放在Map中,默认获取第一路径的所有参数。

6.从matricMap2可以发现,可以精确获取某个路径上的所有参数,并放在Map中。

7..如果required的值为true,并且方法参数没有在路径的参数中找到绑定值时,会报错。

注解总结:上面是我认为用的比较多的注解,还有一部分注解,这里没有介绍。我也是从2022年3月份才系统的学习java,也算是个新手,也是在学习过程中。上面内容,可能有些叙述是不合适的,甚至有些叙述可能就是错误的,我也是在学习过程中,请多包涵。

细心的小伙伴,可能发现,很多内容就是根据源码来总结的,然后自己在去尝试一下,就知道哪些是正确的,哪些是错误的了。当然,上面内容不可能都记住的,要学会自己看看一些简单的源码,我现在也在努力尝试。都说授之以鱼不如授之以渔,下面以@MatrixVariable注解为例,讲一下源码怎么看,如果是大佬这里可以略过,甚至这一篇文章都不用看了。

/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.web.bind.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.core.annotation.AliasFor;

/**
 * Annotation which indicates that a method parameter should be bound to a
 * name-value pair within a path segment. Supported for {@link RequestMapping}
 * annotated handler methods.
 *
 * <p>If the method parameter type is {@link java.util.Map} and a matrix variable
 * name is specified, then the matrix variable value is converted to a
 * {@link java.util.Map} assuming an appropriate conversion strategy is available.
 *
 * <p>If the method parameter is {@link java.util.Map Map&lt;String, String&gt;} or
 * {@link org.springframework.util.MultiValueMap MultiValueMap&lt;String, String&gt;}
 * and a variable name is not specified, then the map is populated with all
 * matrix variable names and values.
 *
 * @author Rossen Stoyanchev
 * @author Sam Brannen
 * @since 3.2
 */
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MatrixVariable {

	/**
	 * Alias for {@link #name}.
	 */
	@AliasFor("name")
	String value() default "";

	/**
	 * The name of the matrix variable.
	 * @since 4.2
	 * @see #value
	 */
	@AliasFor("value")
	String name() default "";

	/**
	 * The name of the URI path variable where the matrix variable is located,
	 * if necessary for disambiguation (e.g. a matrix variable with the same
	 * name present in more than one path segment).
	 */
	String pathVar() default ValueConstants.DEFAULT_NONE;

	/**
	 * Whether the matrix variable is required.
	 * <p>Default is {@code true}, leading to an exception being thrown in
	 * case the variable is missing in the request. Switch this to {@code false}
	 * if you prefer a {@code null} if the variable is missing.
	 * <p>Alternatively, provide a {@link #defaultValue}, which implicitly sets
	 * this flag to {@code false}.
	 */
	boolean required() default true;

	/**
	 * The default value to use as a fallback.
	 * <p>Supplying a default value implicitly sets {@link #required} to
	 * {@code false}.
	 */
	String defaultValue() default ValueConstants.DEFAULT_NONE;

}

在这里看源码,没有在IDEA中舒服。首先看最上面的注释,发现有Map<String,String>这个(在IDEA中很清晰,直接为蓝色),就说明可以用这个类型的map进行接收数据,具体的用法自己尝试。然后看来类上的注解,先看@Target,这个是表示注解用的地方,可以Ctrl+鼠标左键点入ElementType进去看,一个Type代表一个类型,可以看每个Type上面的注解,比如METHOD代表方法,TYPE代表类、接口或者枚举类等。再看紧挨着类的注解,比如@RestController会显示有@Controller和@ResponseBody就代表是这两个注解的结合。

下面就要看类里面了,看看一共有几个属性,一般name属性不用管,value、required和defaultValue一般都会有,看看上面注释就大体知道每个意思,再不行去搜一下。看这个主要的目的就是看看参数个数,一般看到参数名也就才到7788了,比如这里多了个pathVar,大体一猜就知道干什么的了。再比如@RequestBody注解中就一个required属性,我们也可以猜出这个注解每个方法上只能出现一次,因为没有value属性指明参数名。我本来也很讨厌看源码,主要看不懂。学会看源码,就可以记住一下常规情况,不需要记很多内容,真正遇到了,再去查看就好了。哪怕我们什么也不会,看下源码,我们也有方向去搜索内容去学习。尝试一下,看简单源码,你会发现更多的乐趣。

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飘逸飘逸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值