【SSM框架】SpringMVC 中常见的注解和用法

SpringMVC 中常见的注解和用法

基础注解介绍

@RequestMapping 注解介绍

@RequestMapping 注解是用来注册接口的路由映射。

我们来看一下他的基础使用:

@Controller //在Spring 框架 启动的时候 类就加载进去了
@ResponseBody //返回非页面数据
@RequestMapping("/test")
public class TestController {
    //注册接口的路由映射
    @RequestMapping("/hello")
    public String say(){
        return "hello world";
    }
}

此注解可以修饰类和方法。当修饰类的时候访问地址是 类+方法

我的电脑上运行springBoot程序的端口是8080。所以访问地址为:localhost:8080/test/hello

image-20230506215702057

@RequestMapping 直接修饰类:

@Controller //在Spring 框架 启动的时候 类就加载进去了
@ResponseBody //返回非页面数据
public class TestController {
    //注册接口的路由映射
    @RequestMapping("/hello")
    public String say(){
        return "hello world";
    }
}

访问地址为:localhost:8080/hello

@RequestMapping 注解是客户端发送Post和Get请求都可以接受(可以用postMan测试一下)。那么我们业务要求只能接收post请求或者只能接收Get请求我们应该如何处理呢?

我们可以显示指定@RequestMapping来接受post:

@Controller //在Spring 框架 启动的时候 类就加载进去了
@ResponseBody //返回非页面数据
public class TestController {
    //注册接口的路由映射
    @RequestMapping(value = "/hello",method = RequestMethod.POST)
    public String say(){
        return "hello world";
    }
}

我们要是想让他只能接收Get我们只需要将RequestMethod.POST 改为 RequestMethod.GET就可以了。我们如果需要验证是否只能够接收单一模式的请求我们可以借助postman来发送不同类型的请求。

@PostMapping 和 @GetMapping 注解介绍

前面介绍了RequestMapping 想要接收单一类型的请求用指定显示来完成。当然有专门的注解来完成这个需求

只能接收Get 请求的两种注解写法:

@RequestMapping(value = "/hello",method = RequestMethod.GET)

@GetMapping("/hello")

只能接收Post 请求的两种注解写法:

@RequestMapping(value = "/hello",method = RequestMethod.POST)

@PostMapping("/hello")

获取参数相关注解的介绍

只通过 @RequestMapping 来获取参数

只传递一个参数

springMVC可以直接用方法中的参数来实现传参:

@Controller //在Spring 框架 启动的时候 类就加载进去了
@ResponseBody //返回非页面数据
public class TestController {
    @RequestMapping("/index")
    public String say4(int id){
        return "id = " + id;
    }
}

在网址栏里直接输入:localhost:8080/index?id=123。便可得知已经成功获取参数 。

image-20230506234537095

直接传递基本数据类型的话,如果参数未传或者参数错误直接就会报错:

image-20230506235238089

所以参数最好不要用基本数据类型,传递过来的数据是基本数据类型的话可以用包装类来接收。

@Controller //在Spring 框架 启动的时候 类就加载进去了
@ResponseBody //返回非页面数据
public class TestController {
    @RequestMapping("/index")
    public String say(Integer id){
        if (id == null){
            return "参数错误";
        }
        return "id = " + id;
    }
}

用包装类来接收基本数据类型可以在方法内部来处理参数错误的情况:

image-20230506235651190

传递对象参数

SpringMVC可以自动实现参数对象的赋值,比如我创建一个Userinfo对象

import lombok.Data;
@Data
public class UserInfo {
    private int id;
    private String name;
    private String password;
    private int age;
}

传递对象代码实现:

@RequestMapping("/index")
public String method(Integer id){
    if (id == null){
        return "参数错误";
    }
    return "id = " + id;
}

用postman发送请求:

image-20230508195624660

通过返回的结果可知,发送的键值对中键的名称是和对象中的属性名称是一样的,不传递的参数是设置其为默认值。

传递多个参数(非对象)

传递多个参数的代码:

@RequestMapping("/index2")
public Object method(String name,Integer id){
    return "name: " + name + " id:" + id;
}

通过postman来发送请求进行访问:

image-20230508201406187

前后端进⾏参数匹配时,是以参数的名称进⾏匹配的 ,参数的位置不影响后端获取参数的结果

@RequestParam 后端参数重命名

前后端进⾏参数匹配时,是以参数的名称进⾏匹配的。我们在进行前后端交互的时候可能后端人员写完代码了,在某一处参数的名称为name,但是前端人员要求发送的名称是username,后端人员直接修改名字的话就变化太多了,我们可以用@RequestParam注解来实现重命名。

重命名的代码:

@RequestMapping("/index3")
public Object method2(@RequestParam("username") String name,Integer id){
    return "name: " + name + " id:" + id;
}

通过postman来发送请求进行访问:

image-20230508202719314

但是此代码还会有一个问题,不用@RequestParam进行重命名的时候,参数少传递是没有问题的,没有接收到参数的直接赋值为默认值。但是重命名之后参数少传我们会直接报错。

image-20230508203300208

required 必传参数的设置

@RequestParam的必传参数的设置默认为true,我们要想该参数非必传的话就需要修改required

image-20230508205348077

修改required:

    @RequestMapping("/index3")
    public Object method2(@RequestParam(value = "username",required = false) String name,Integer id){
        return "name: " + name + " id:" + id;
    }

修改完成之后参数就可以非必传了。

### @RequestBody 接收JSON对象

后端代码:

@RequestMapping("/index5")
public Object method4(@RequestBody UserInfo userInfo){
    return userInfo;
}

用postman发送请求:

image-20230508215515701

如果没有@RequestBody注解 userInfo中的属性就不会接收到参数

@PathVariable 获取URL中的参数

后端实现代码:

@RequestMapping("/index4/{name}/{password}")
public Object method3(@PathVariable String name,@PathVariable String password){
    return "name:  "+name + "  |password:  " + password;
}

用postman发送请求访问后端:

image-20230508211143745

但是需要注意的就是URL中的顺序变了,后端接收的参数是按着顺序来的.

还要注意的一点就是@PathVariable 的必传参数的设置默认为true,要是想要设置为非必传的话修改为required = false也是没有效果的

@RequestMapping中{}中的单词和参数的名称是对应的,如果{}中的名称password变成了pwd,没必要方法的形参也改名只需要value = “pwd” 重命名就可以了

@RequestMapping("/index4/{name}/{pwd}")
public Object method3(@PathVariable String name,@PathVariable(value = "pwd",required = false) String password){
    return "name:  "+name + "  |password:  " + password;
}

image-20230508214347132

但是如果URL改为http://localhost:8080/index4/zhangsan/123456 如果 {}中写password 参数写pwd不会报错,但是输出结果为null.

image-20230508213731623

如果参数URL少写肯定会报错

@RequestPart 上传文件

后端代码:

@RequestMapping("/myupload")
public Object upload(@RequestPart("myimg") MultipartFile file){
    //文件名称  UUID
    String fileName = UUID.randomUUID()//文件名 用UUID而不是直接命名是为了防止文件覆盖只能保存一个文件
            + file.getOriginalFilename().substring(
            file.getOriginalFilename().indexOf("."));//文件后缀
    //文件保存地址
    File saveFile = new File("D:\\data\\"+fileName);
    try {
        //保存文件
        file.transferTo(saveFile);
        return true;
    } catch (IOException e) {
        e.printStackTrace();
    }
    return false;
}

用postman发送请求访问后端:

image-20230508233728430

在目的地址看到了传送的文件:

image-20230508233824805

倘若文件大于springMVC的默认要求大小,我们可以修改 application.properties 配置文件 让其能传送的数据更大:

spring.servlet.multipart.max-file-size=100MB
spring.servlet.multipart.max-request-size=100MB

@CookieValue 简洁的获取 Cookie

后端代码:

@RequestMapping("/getck")
public Object getCk(@CookieValue(value = "java",required = false) String java){
    return java;
}

如果直接访问localhost:8080/getck页面不会有任何结果,因为当前页面没有Cookie 。我们只需要在页面中按Fn + F12 就加一个Cookie 然后刷新就可以看到cookie的返回值。

image-20230509104559905

@RequestHeader 简洁获取 Header

后端代码:

@RequestMapping("/gethd")
public Object getHeader(@RequestHeader("User-Agent") String ua){
    return "User-Agent : " + ua;
}

前端发送的:

image-20230509121329229

@SessionAttribute 简洁获取Session

我们获取Session之前我们应该先存储相对应的Session:

private static final String SESSION_KEY = "USERINFO_SESSION_KEY";
@RequestMapping("setsess")
public Object doPostConstruct(HttpServletRequest request){
    HttpSession session = request.getSession();
    session.setAttribute(SESSION_KEY,"张三");
    return "存储Session成功";
}

简洁获取Session 的代码:

@RequestMapping("/getsess")
public Object getSession(@SessionAttribute(SESSION_KEY) String name){
    return "session" + name;
}

我们先访问localhost:8080/setsess再访问localhost:8080/getsess就可以查看获取到的session了

SpringMVC的返回数据

前面我们用到的@ResponseBody对于前端你来说返回的是数据, 默认请求下⽆论是 Spring MVC 或者是 Spring Boot 返回的是视图(xxx.html)。我们接下来详细了解一下SpringMVC数据返回

@ResponseBody 注解介绍

@ResponseBody的功能:

  • 返回的值如果是字符会转换成 text/html,如果返回的是对象会转换成application/json 返回给前端。

  • 可以⽤来修饰⽅法或者是修饰类,修饰类表示类中的所有⽅法都会返回 html 或者json,⽽不是视图。

在SSM框架的使用中@Controller 和 @ResponseBody 注解经常一起使用所以有一个注解:

@RestController = @Controller + @ResponseBody

@Controller --> 将类加载到Spring容器中

@ResponseBody --> 返回非页面数据

下面就根据应用 @ResponseBody返回 text/html 和 json。以及不用@ResponseBody注解返回的数据的知识。

返回 text/html

这种情况是需要看浏览器的识别,浏览器自动分析数据转化为html格式。

注意此格式必须加上ResponseBody注解

@ResponseBody//此注解在类上写了在此处可以不写
@RequestMapping("/index6")//页面返回的是text/html
public Object method6(UserInfo userInfo){
    return "<h1>返回text/html</h1>";
}

返回 JSON 对象

@ResponseBody//此注解在类上写了在此处可以不写
@RequestMapping("/index7")//页面返回的是text/html
public Object method7(){
    Map<String,String> map = new HashMap<>();        
    map.put("id","1");
    map.put("name","zhangsan");
    map.put("passwoerd","123456");
    return map;
}

浏览器访问:

image-20230511115605119

练习:实现计算器功能

对于访问后端,可使⽤ postman 传递参数,或使⽤ form 表单的⽅式提交参数:

我们这里使用前端端的form表单来实现提交参数:

<!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>实现计算机功能</title>
</head>
<body>
    <form action="http://localhost:8080/test/sum">
        <h1>计算机</h1>
        数字1: <input name="num1" type="text"><br>
        数字2: <input name="num2" type="text"><br>
        <input type="submit" value="点击相加">
    </form>
</body>
</html>

后端代码:

@RestController
public class TestController {
    @RequestMapping("/sum")
    public Object add(Integer num1,Integer num2){
        return String.format("<h1>计算的结果是:%d</h1><a href='javascript:history.go(-1);'>返回</a>",num1+num2);
    }
}

返回静态页面

我们需要注意的是前端页面写的位置是在:

image-20230511164107969

注意此种情况返回的是视图,这里的index.html是我上面刚写的计算器功能:

@Controller //在Spring 框架 启动的时候 类就加载进去了
@RequestMapping("test")
public class TestController2 {
    @RequestMapping("/index")
    public Object method(){
        return "/index.html";
    }
}

我们拥有浏览器访问后端接口:

image-20230511163917145

我们为什么要在返回值中写一个 / 呢?这个是因为返回的是前端的路径,/ 代表直接在static文件夹中。不加 / 的话访问的就是不是直接在static文件夹中的index.html了。

我们改为返回"index.html"查看浏览器访问结果:

image-20230511163840118

请求转发和请求重定向

return 不但可以返回⼀个视图,还可以实现跳转,跳转的⽅式有两种:

  • forward: 是请求转发

  • redirect:请求重定向

请求转发和请求重定向的差别

请求转发和请求重定向的代码:

//请求转发
@RequestMapping("/index2")
public Object method2(){
    return "forward:/index.html";
}
//请求重定向
@RequestMapping("/index3")
public Object method3(){
    return "redirect:/index.html";
}

下面就访问一下,看看请求转发和请求重定向的区别:

image-20230511171137839

可以看到访问index2也就是请求转发,访问的路径没变。而访问index3也就是请求重定向,访问路径改变。

我们可以用fidder抓包来看一下具体的访问过程:

image-20230511172328593

我们可以明显看出,请求转发是访问服务器,服务器端转发。请求重定向是访问服务器, 服务器返回给客户端网址,客户端访问这个网址。

我们可以这样想:请求转发是你想要申请设备,向组织申请,公司直接把设备采购好给你。请求重定向是想要申请设备,向公司申请,公司给你钱,你自己去买设备。

请求转发 forward 导致的问题

请求转发如果资源和转发的⻚⾯不在⼀个⽬录下,会导致外部资源不可访问,演示示例如下 :

程序的目录和改编如下:

image-20230511181803709

程序的执⾏结果如下:

image-20230511181728868

尝试将转发 foward 换成重定向 redirect ,⻚⾯就可以正常获取到外部资源 js 了 。

请求转发和请求重定向的区别

  • 请求重定向(redirect)将请求重新定位到资源;请求转发(forward)服务器端转发。
  • 请求重定向地址发⽣变化,请求转发地址不发⽣变化
  • 请求重定向与直接访问新地址效果⼀样,不存在原来的外部资源不能访问;请求转发服务器端转发有可能造成原外部资源不能访问。

小tips: 我们可以帮助我们记忆这两个名词: 请求转发的字数小于请求重定向,字越少事情就越多,请求转发服务器干的事情多:服务器自己得转发。请求重定向服务器干的事情少:直接返回地址让其直接访问。

  • 0
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值