SpringMVC详解

发布于:2025-07-05 ⋅ 阅读:(14) ⋅ 点赞:(0)

目录

1 MVC

2 创建Spring MVC项目

3 建立连接

3.1 @RestController

3.2 @RequestMapping

4 请求

4.1 请求中有单个参数

4.2 请求中的参数类型

4.3 请求中有多个参数

4.4 请求中传递对象

4.5 请求中参数的重命名

4.6 请求中传递数组

4.7 请求中传递集合

4.8 请求中传递JSON

4.9 请求中URL中的参数

4.10 请求中传递文件

4.11 获取Cookie

(1)方式1:传统Servlet获取

(2)方式2:Spring中获得请求中Cookie的方式

4.12 获取Session

(1)方式1:传统Servlet获取

(2)方式2:从Spring MVC内置对象HttpSession来获取

(3)方式3:@SessionAttribute注解把Session的值直接赋值给方法的参数

4.13 请求中获取Header

5 响应

5.1 返回页面

5.2 @RestController与@Controller的区别与联系

(1)@RestController源码

(2)@Controller源码

(3)@ResponseBody源码

(4)区别与联系

5.3 返回数据@ResponseBody

5.4 返回html代码片段

5.5 返回json

5.6 返回js与css

5.7 设置状态码

5.8 设置Header

6 lombok工具

6.1 引入依赖

(1)创建时引入

(2)pom.xml手动引入

6.2 lombok使用

6.3 lombok的细粒度注解


        Spring MVC全名是Spring Web MVC,它是基于Servlet API构建的Web框架,也是实现MVC思想的框架。

1 MVC

        MVC思想是一种分模块处理的思想,比如常见的网站,浏览器页面是View视图,Controller控制器是请求发送给服务器处理请求的地方,而Model模型则是核心的业务逻辑,比如请求希望从数据库读取数据,那么Model层就负责和数据库的交互。

        由于MVC思想在现在已经被前后端分离的思想替代,后端不需要关注视图层,因此学习SpringMVC主要是学习Web框架的用法。

2 创建Spring MVC项目

        创建SpringMVC项目的方式是通过创建SpringBoot项目,引入Spring Web依赖(Spring Web MVC),即可创建。创建流程详细见SpringBoot系列:

        Spring Web的用法主要是:1.建立连接。2.处理请求。3.返回响应。

3 建立连接

@RestController

public class UserController {

    // 路由器规则注册

    @RequestMapping("/helloMVC")

    public String hello(){

        return "hello,Spring MVC";

    }

}

3.1 @RestController

        在这里,@RestController注解和@RequestMapping注解经常是搭配使用的。@RestController注明了该类就是Controller类,可以理解为mvc思想中的Controller,在这个类中处理请求和返回响应。只有添加了这个注解,Spring收到请求后才会在该类中查看有无对应的url和处理方法。

3.2 @RequestMapping

        (1)@RequestMapping注解提供了URL的映射关系,用于注册接口的路由映射(用户请求的URL和项目中某个类的某个方法对应)。它有两种作用域:

        1.作用于类

@RequestMapping("/MVC")

@RestController

public class UserController {

    // 路由器规则注册

    @RequestMapping("/helloMVC")

    public String hello(){

        return "hello,Spring MVC";

    }

}

        此时用户要访问的URL就变为:类路径+方法路径,即ip地址:8080/MVC/helloMVC。

        2.作用于方法

@RestController

public class UserController {

    // 路由器规则注册

    @RequestMapping("/helloMVC")

    public String hello(){

        return "hello,Spring MVC";

    }

}

        此时用户要访问的URL是:方法路径,即ip地址:8080/helloMVC。

        注意:@RequestMapping注解可以写多级路径,同时也可以省略第一级路径前的/(推荐不省略),如果没有Spring会自动拼接。

        (2)@RequestMapping既支持Get请求,又支持Post请求,也支持其他的请求方式。

        @RequestMapping(value = "/getRequest",method= RequestMethod.POST)可以使用这样的方式指定请求的方法。

4 请求

        由于Servlet API在使用过程中,经常重复多次使用,比如获取请求中参数的值:HttpServletRequest req和req.getParameter()。因此Spring为了简化开发流程,就把重复性的事封装到框架中,获取参数直接通过方法的参数获取

4.1 请求中有单个参数

    @RequestMapping("/oneParameter")

    public String oneParameter(String name){

        return "单个参数的值:" + name;

    }

        这里需要注意,方法的参数name必须和请求的参数的键一样。

4.2 请求中的参数类型

    @RequestMapping("/int")

    public String intMethod(int age){

        return "单个参数的值:" + age;

    }

        但是当我们不传参数age时,就会发生下面的现象:

        服务器报错,提示age是基本数据类型,默认值为0。但是不传参数默认值为null,null无法被转化为基本类型。因此,如果要传的参数存在空值的情况,推荐使用包装类型参数。

    @RequestMapping("/integer")

    public String integerMethod(Integer age){

        return "单个参数的值:" + age;

    }

        注意:如果传其他类型的参数,可能会出现404。比如传String,就无法转化成Integer。

4.3 请求中有多个参数

    @RequestMapping("/mulParameter")

    public String mulParameter(String name, Integer age){

        return "name:" + name + " age:" + age;

    }

        传递多个参数不需要指定顺序,只需要确保键和方法的参数对应。

4.4 请求中传递对象

@RequestMapping("/object")
public String object(UserInfo user){

    return user.toString();

}

public class UserInfo {

    private Integer id;

    private String name;

    private Integer age;



    public Integer getId() {

        return id;

    }



    public void setId(Integer id) {

        this.id = id;

    }



    public String getName() {

        return name;

    }



    public void setName(String name) {

        this.name = name;

    }



    public Integer getAge() {

        return age;

    }



    public void setAge(Integer age) {

        this.age = age;

    }



    @Override

    public String toString() {

        return "UserInfo{" +

                "id=" + id +

                ", name='" + name + '\'' +

                ", age=" + age +

                '}';

    }

}

        请求中如果传递对象,那请求的写法就依次写对象属性的键值对即可,Spring会自动把参数键值对与对象的属性和值对应。

        注意:如果传递的参数个数少于对象的属性数量,未传参的属性就会被Java赋值为默认值(Java做的事)。因此,如果对象中使用基本数据类型,就不会出现null无法赋值为int等类似的情况了,而是int默认被赋值为0。

4.5 请求中参数的重命名

    @RequestMapping("/rename")

    public String rename(@RequestParam("name") String username, Integer age){

        return "username:" + username + " age:" + age;

    }

        当方法中参数名不希望使用请求中的参数名时,可以进行重命名。在方法参数中希望重命名的参数前添加@RequestParam("name")注解,"name"是请求中的参数名,username是重命名后的参数名。

        观察@RequestParam注解的源码实现:

        注意:当添加@RequestParam注解后,required属性默认为true,这说明重命名后的参数变成了必传参数,如果前端不传这个参数,就会报错。要把参数变为非必传参数,就需要在注解中添加属性@RequestParam(value = "name",required = false)。

4.6 请求中传递数组

@RequestMapping("/arr")

public String arr(String[] array){

    return "arr=" + Arrays.toString(array) + ",length=" + array.length;

}

        请求中传递数组有两种方式,1.参数名=值,值,值,......2.参数名=值&参数名=值&参数名=值......。

4.7 请求中传递集合

    @RequestMapping("/list")

    public String list(@RequestParam("list") List<Integer> list){

        return "list=" + list.toString() + ",length=" + list.size();

    }

        请求中传递集合,必须使用@RequestParam("list")和方法中的参数进行绑定(因为多个相同的参数名的多个值默认封装为数组)。同理,如果不是必传参数,也要加上required = false。

4.8 请求中传递JSON

    @RequestMapping("/json")

    public String json(@RequestBody UserInfo userInfo){

        return userInfo.toString();

    }

        传递json参数时,json字符串被放在请求的Body中,因此要获取json,就要在json反序列化为对象的参数前添加@RequestBody注解,表示从请求的Body中获取传递的参数。

        注意:SpringBoot项目集成了json的依赖jackson,因此不需要再手动添加依赖到pom.xml中。

        注意:在反序列化时,如果对象的类中只写了含参构造方法,此时默认的无参构造方法就会消失,因此json字符串反序列化为对象时,由于Spring内部调用的是无参构造方法,于是就会出现jackson报出的异常:com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.example.springmvc.UserInfo` (no Creators, like default constructor, exist)。

    public UserInfo(Integer id, String name, Integer age) {

        this.id = id;

        this.name = name;

        this.age = age;

    }

        要解决该问题,就需要给出含参构造方法时,保留无参构造方法。

    public UserInfo() {

    }

4.9 请求中URL中的参数

        这里并不是指获取?以后的参数,而是获取?前的某个路径。

    @RequestMapping("/urlParameter/{name}/{age}")

    public String urlParameter(@PathVariable("name") String username, @PathVariable()Integer age){

        return "username:" + username + ",age:" + age;

    }

        需要使用到@PathVariable注解,该注解和@RequestParam注解类似,可以重命名,可以选择参数是否是必传的(尽量不修改,就让参数是必传的,否则就容易出现请求路径和方法的url不匹配)。

        当需要从url获取参数时,就要约定方法的url格式:/路径/{要获取的参数}/......。同时要获取的参数名必须和方法中参数名对应,且必须加上@PathVariable注解。

4.10 请求中传递文件

    @RequestMapping("/fileUpload")

    public String fileUpload(@RequestPart MultipartFile file) throws IOException {

        String filename = file.getOriginalFilename();

        file.transferTo(new File("D:\\FileTest\\" + filename));

        return "文件上传成功:" + filename;

}

        文件上传需要用到MultipartFile类来存储文件对象,可以不使用@RequestPart注解(建议加上,该注解也可以进行重命名等),上传的文件需要用到transferTo()来把文件对象保存到File对象所指向的路径。

4.11 获取Cookie

(1)方式1:传统Servlet获取

        优点:可以获取到全部的Cookie。

    @RequestMapping("/getCookie1")

    public String getCookie1(HttpServletRequest request) {

        // Servlet中获得请求中Cookie的方式(可以获得所有的Cookie)

        Cookie[] cookies = request.getCookies();

        // lambda表达式循环所有的cookie

        if(cookies != null){

            Arrays.stream(cookies).forEach(cookie -> {

                System.out.println(cookie.getName() + ":" + cookie.getValue());

            });

            return "获取cookie成功" ;

        }

        return "cookie为null" ;

    }

        首先需要在Postman中创建Cookie,否则获取到的cookies就为null。

(2)方式2:Spring中获得请求中Cookie的方式

        但是只能获取使用@CookieValue注解了的参数对应的值。

    @RequestMapping("/getCookie2")

    public String getCookie2(@CookieValue("username") String username) {

        // Spring中获得请求中Cookie的方式(只能获得参数列表用@CookieValue注解的Cookie)

        return "username : " + username;

    }

4.12 获取Session

(1)方式1:传统Servlet获取

    @RequestMapping("/setSession")

    public String setSession(HttpServletRequest request) {

        HttpSession session = request.getSession();

        session.setAttribute("username","zhangsan");

        return "设置Session成功";

    }

    @RequestMapping("/getSession1")

    public String getSession1(HttpServletRequest request) {

        // 方式1:Servlet中从HttpServletRequest中获取session

        // 如果session不存在, 不会自动创建

        HttpSession session = request.getSession(false);

        String username = null;

        if (session != null && session.getAttribute("username") != null) {

            username = (String) session.getAttribute("username");

        }

        return "username:" + username;

    }

        首先需要设置Session:

(2)方式2:从Spring MVC内置对象HttpSession来获取

    @RequestMapping("/getSession2")

    public String getSession2(HttpSession session) {

        // 方式2:从Spring MVC内置对象HttpSession来获取

        String username = null;

        if (session != null && session.getAttribute("username") != null) {

            username = (String) session.getAttribute("username");

        }

        return "username:" + username;

    }

(3)方式3:@SessionAttribute注解把Session的值直接赋值给方法的参数

    @RequestMapping("/getSession3")

    public String getSession3(@SessionAttribute(value = "username",required = false) String username) {

        // 方式3:@SessionAttribute注解把Session的值直接赋值给username

        return "username:" + username;

    }

4.13 请求中获取Header

    @RequestMapping("/getHeader1")

    public String getHeader1(HttpServletRequest request) {

        // 方式1:传统Servlet获取Header

        String userAgent = request.getHeader("User-Agent");

        return "User-Agent:" + userAgent;

    }

    @RequestMapping("/getHeader2")

    public String getHeader2(@RequestHeader("User-Agent") String userAgent) {

        // 方式2:SpringMVC中使用@RequestHeader注解获取指定Header的键值对

        return "User-Agent:" + userAgent;

    }

        获取Header也有传统Servlet的方式和SpringMVC注解的方式,使用@RequestHeader注解需要填写需要获取的Header的键的名字,并将值赋给方法的参数。

5 响应

5.1 返回页面

@RestController

@RequestMapping("/respMVC")

public class ResponseController {

    @RequestMapping("/returnHTML")

    public String returnHTML(){

        return "/index.html";

    }

}

        这里返回页面的路径写法是从/resources/static开始,如果页面被放在该目录的下一级目录,就需要加上目录名,且路径前的/不能省略

        当使用@RestController,返回的其实不是页面,而是数据:

        如果想要返回页面,就需要把@RestController替换成@Controller注解:

@Controller

@RequestMapping("/respMVC")

public class ResponseController {

    @RequestMapping("/returnHTML")

    public String returnHTML(){

        return "/index.html";

    }

}

        那么这两个注解的区别和联系到底是什么?

5.2 @RestController与@Controller的区别与联系

(1)@RestController源码

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Controller

@ResponseBody

public @interface RestController {

    @AliasFor(

        annotation = Controller.class

    )

    String value() default "";

}

        @Target({ElementType.TYPE})、@Retention(RetentionPolicy.RUNTIME)、@Documented这三个注解是元注解。

        其中@Target注解标识了注解的范围,TYPE类型表示类、接口或枚举等等。

        @Retention注解标识注解的生命周期:SOURCE(源码时期)、CLASS(字节码时期)和RUNTIME(运行时期)。

        @Documented注解用于该注解标识的内容应该包含在生成的Javadoc文档中。

        剩下两个注解则是我们需要注意的重点:

(2)@Controller源码

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Component

public @interface Controller {

    @AliasFor(

        annotation = Component.class

    )

    String value() default "";

}

        @Controller注解即是为@RestController提供请求管理功能(根据请求的URL,在所有@RestController注解下的类寻找请求的方法的路径)的注解,定义一个控制器,Spring框架启动时加载,把这个对象交给Spring管理。该注解标识的类下,所有的方法返回的数据类型均是html页面。因此只有用该注解,我们的Controller才能返回页面。

(3)@ResponseBody源码

@Target({ElementType.TYPE, ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface ResponseBody {

}

        @ResponseBody注解即是为@RestController提供返回数据功能的注解。该注解可以标注在类(类中所有的方法都返回数据),也可以标注在方法(该方法返回数据)。返回的数据类型是非视图,即text/html。因此用该注解,我们的Controller才能在返回的响应中填入数据。

(4)区别与联系

        @RestController=@Controller+@ResponseBody因此@RestController既有请求管理的功能,又具有返回数据的功能,但是不具有返回视图(html页面)的功能。

        如果想要返回页面,在类注解上@Controller。如果想要返回数据,直接使用@RestController注解或@ResponseBody注解。在一个类中有些方法返回页面,有些方法返回数据,那就给这个类加上@Controller,给想要返回数据的方法加上@ResponseBody。

5.3 返回数据@ResponseBody

    @ResponseBody

    @RequestMapping("/returnData")

    public String returnData(){

        return "/index.html";

    }

5.4 返回html代码片段

    @ResponseBody

    @RequestMapping("/returnHTMLPart")

    public String returnHTMLPart(){

        return "<h1>html片段</h1>";

    }

5.5 返回json

    @ResponseBody

    @RequestMapping("/returnJSON1")

    public UserInfo returnJSON1(){

        UserInfo userInfo = new UserInfo(100,"zhangsan",20);

        return userInfo;

    }

    @ResponseBody

    @RequestMapping("/returnJSON2")

    public Map<String,String> returnJSON2(){

        Map<String,String> map = new HashMap<>();

        map.put("name","zhangsan");

        return map;

    }

        返回json有多种方式,这是SpringMVC进行的响应数据的动态类型转换,对象、HashMap等具有键值对都可以自动被转化为json字符串。在响应中对应的Content-Type的值为application/json。

5.6 返回js与css

    @RequestMapping("/returnJS")

    public String returnJS(){

        return "/a.js";

    }

    @RequestMapping("/returnCSS")

    public String returnCSS(){

        return "/b.css";

    }

        返回js和css也是属于视图类型的,因此需要用到@Controller注解,去掉@ResponseBody注解。返回的响应中Content-Type的值分别为text/css和application/javascript。

5.7 设置状态码

    @ResponseBody

    @RequestMapping("/returnStatus")

    public String returnStatus(HttpServletResponse response){

        response.setStatus(404);

        return "设置状态码成功";

    }

        需要注意,状态码的设置并不影响页面的展示。

5.8 设置Header

    @ResponseBody

    @RequestMapping("/returnHeader")

    public String returnHeader(HttpServletResponse response){

        response.setHeader("MyHeaderKey","MyHeaderValue");

        return "设置Header成功";

    }

6 lombok工具

        lombok工具是一系列注解的工具包,用于自动提供数据类Getter、Setter等方法。

6.1 引入依赖

(1)创建时引入

(2)pom.xml手动引入

        可以去Maven查询网站https://mvnrepository.com搜索lombok引入依赖:

<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->

<dependency>

    <groupId>org.projectlombok</groupId>

    <artifactId>lombok</artifactId>

    <version>1.18.30</version>

    <scope>provided</scope>

</dependency>

        也可以下载插件EditStarters,在插件中引入:

        下载好插件后,打开pom.xml文件,右键选择Generate,点击Edit Starters,再选择一个网络通畅的源进行下载。

6.2 lombok使用

@Data

public class UserInfo {

    private Integer id;

    private String name;

    private Integer age;

    public UserInfo() {

    }

    public UserInfo(Integer id, String name, Integer age) {

        this.id = id;

        this.name = name;

        this.age = age;

    }

}

        在数据类前加上@Data注解,lombok就会自动生成Getter、Setter等方法并和Java源文件组合到一起编译成.class文件。观察反编译后的字节码文件:

public class UserInfo {

    private Integer id;

    private String name;

    private Integer age;



    public UserInfo() {

    }



    public UserInfo(Integer id, String name, Integer age) {

        this.id = id;

        this.name = name;

        this.age = age;

    }



    public Integer getId() {

        return this.id;

    }



    public String getName() {

        return this.name;

    }



    public Integer getAge() {

        return this.age;

    }



    public void setId(Integer id) {

        this.id = id;

    }



    public void setName(String name) {

        this.name = name;

    }



    public void setAge(Integer age) {

        this.age = age;

    }



    public boolean equals(Object o) {

        if (o == this) {

            return true;

        } else if (!(o instanceof UserInfo)) {

            return false;

        } else {

            UserInfo other = (UserInfo)o;

            if (!other.canEqual(this)) {

                return false;

            } else {

                label47: {

                    Object this$id = this.getId();

                    Object other$id = other.getId();

                    if (this$id == null) {

                        if (other$id == null) {

                            break label47;

                        }

                    } else if (this$id.equals(other$id)) {

                        break label47;

                    }



                    return false;

                }



                Object this$age = this.getAge();

                Object other$age = other.getAge();

                if (this$age == null) {

                    if (other$age != null) {

                        return false;

                    }

                } else if (!this$age.equals(other$age)) {

                    return false;

                }



                Object this$name = this.getName();

                Object other$name = other.getName();

                if (this$name == null) {

                    if (other$name != null) {

                        return false;

                    }

                } else if (!this$name.equals(other$name)) {

                    return false;

                }



                return true;

            }

        }

    }



    protected boolean canEqual(Object other) {

        return other instanceof UserInfo;

    }



    public int hashCode() {

        int PRIME = true;

        int result = 1;

        Object $id = this.getId();

        result = result * 59 + ($id == null ? 43 : $id.hashCode());

        Object $age = this.getAge();

        result = result * 59 + ($age == null ? 43 : $age.hashCode());

        Object $name = this.getName();

        result = result * 59 + ($name == null ? 43 : $name.hashCode());

        return result;

    }



    public String toString() {

        return "UserInfo(id=" + this.getId() + ", name=" + this.getName() + ", age=" + this.getAge() + ")";

    }

}

        这些没有定义的方法都是lombok使用@Data注解自动生成的。

6.3 lombok的细粒度注解

注解

作用

@Getter

动添加getter方法(放在想要添加的属性前)

@Setter

动添加setter方法(放在想要添加的属性前)

@ToString

动添加toString方法

@EqualsAndHashCode

动添加equals和hashCode方法

@NoArgsConstructor

动添加参构造方法

@AllArgsConstructor

动添加参构造方法,顺序是属性定义顺序

@NonNull

属性不能为Null

@RequiredArgsConstructor

动添加必需属性的构造方法,final+@NonNull的属性为必需

        而@Data = @Getter + @Setter + @ToString + @EqualsAndHashCode + @RequiredArgsConstructor + @NoArgsConstructor。


网站公告

今日签到

点亮在社区的每一天
去签到