Spring MVC

Spring MVC

一、基本介绍

​ 属于Spring FrameWork的web模块,Spring MVC 分离了控制器、模型对象、过滤器以及处理程序对象的角色,这种分离让它们更容易进行定制。

二、搭建环境

1.新建Maven项目,修改项目打包方式为war,添加项目依赖
<packaging>war</packaging>

<dependencies>
    <dependency>
    	<groupId>org.springframework</groupId>
         <artifactId>spring-webmvc</artifactId>
         <version>5.3.16</version>
    </dependency>
</dependencies>
2.修改项目结构

在这里插入图片描述

在这里插入图片描述

项目中常出现webapp目录结构即为成功

在这里插入图片描述

3.编写web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
         http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--配置欢迎页-->
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

    <!--配置前端控制器-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

        <!--默认情况下,DispatcherServlet会去/WEB-INF/目录下去找名为SpringMVC-servlet.xml文件-->
        <!--自定义XML文件名和路径-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>

        <!--指定优先级,优先加载-->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>
4.编写spring-mvc.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <!--扫描包下面的组件-->
    <context:component-scan base-package="com.qingsongxyz"/>

       
    <!--开启mvc注解驱动自动配置 处理器映射器 和 处理器适配器 -->
    <mvc:annotation-driven/>
  
    <!--下面的两个bean可以省略-->
    -----------------------------------------------------------------------------------
    <!--处理器映射器-->
    <!--在Bean的name与浏览器的url之间建立一种映射关系-->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>

    <!--处理器适配器-->
    <!--将request转换为一个ModelAndView-->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

    ------------------------------------------------------------------------------------
    
    <!--配置视图解析器-->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--如果controller返回的字符串为"index",则去查找/webapp/index.html-->
        <property name="prefix" value="/"/>
        <property name="suffix" value=".html"/>
    </bean>
</beans>
5.编写Controller层和index.html
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class HelloController {

    @RequestMapping("/")
    public String hello(){
        return "index";
    }
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>欢迎来到主页!</h1>
</body>
</html>

6.配置tomcat服务器
在这里插入图片描述在这里插入图片描述

6.运行项目

在这里插入图片描述

三、Controller接受前端参数问题(浏览器400 异常处理)

1.前端传递参数名与后端不一致
<form action="/doLogin" method="post">
    <div class="username">
        用户名:<input type="text" name="name">
    </div>
    <div class="password">
        密码:<input type="text" name="pwd">
    </div>
    <button type="submit">登录</button>
</form>

在这里插入图片描述

@Controller
public class HelloController {

    @RequestMapping("/doLogin")
    public String doLogin(String username, String password)
    {
        System.out.println("username=" + username + " password=" + password);
        //username=null password=null
        return "index";
    }
}

使用@RequestParam注解标记参数,value属性填写前端的参数名

@RequestMapping("/doLogin")
public String doLogin(@RequestParam(value = "name") String username, @RequestParam(value = "pwd") String password)
{
    System.out.println("username=" + username + " password=" + password); 
    //username=a password=bvf4
    return "index";
}
2.日期参数格式化

接受日期参数默认格式为 yyyy/MM/dd HH:mm:ss
在这里插入图片描述

@RequestMapping("/date")
public String date(Date date)
{
    System.out.println("date= " + date);
    //date= Fri Apr 08 16:17:00 CST 2022
    return "index";
}

使用@DateTimeFormat注解,pattern属性设置前端传递日期的格式

在这里插入图片描述

@RequestMapping("/date")
public String date(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date date)
{
    System.out.println("date= " + date);
    return "index";
}
3.接受前端传递json格式参数

导入解析json格式的依赖(Spring MVC默认使用jackson)

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.13.1</version>
</dependency>

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    private Integer id;

    private String name;

    private Integer age;

    private String gender;
}

在接受参数前加上@RequestBody注解,获取前端传递的json格式数据,并将它转换成pojo类

@RequestMapping("/user")
public String getUser(@RequestBody User user){
    System.out.println(user);
    //User(id=1, name=tom, age=18, gender=male)
    return "index";
}

使用Postman进行测试:
在这里插入图片描述

传递json数据之中存在日期参数

必须在日期属性上添加@JsonFormat注解,指明日期格式后,后端才能接受,否则无法自动解析产生400

此时使用@DateTimeFormat(用于表单传递日期参数)注解无效

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    private Integer id;

    private String name;

    private Integer age;

    private String gender;

    //@DateTimeFormat无效
    //@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") //东8时区
    private Date birthday;
}

测试结果: User(id=1, name=tom, age=18, gender=male, birthday=Sat Apr 09 17:36:00 CST 2022)

在这里插入图片描述

4.接受list集合参数(使用json传参)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Department {

    private List<User> userList;
}
@RequestMapping("/userList")
public String getUserList(@RequestBody Department department){
    for (User user :department.getUserList()) {
        System.out.println(user);
    }
    /*
    User(id=1, name=tom, age=18, gender=male, birthday=Sat Apr 09 17:36:00 CST 2022)
    User(id=2, name=marry, age=19, gender=female, birthday=Sat Apr 09 17:36:00 CST 2022)
    User(id=3, name=Bob, age=30, gender=male, birthday=Sat Apr 09 17:36:00 CST 2022)
    */
    return "index";
}

json数据中userList参数名必须和Department类里面的集合属性userList名相同
在这里插入图片描述

5.动态传参

在@RequestMapping中编写动态url,使用{}包裹起来

使用@PathVariable注解获取url中的参数

@RequestMapping("/path/{id}/{name}")
public String dynamic(@PathVariable Integer id, @PathVariable String name){
    System.out.println("@id = "+ id + " name =" + name);
    //@id = 1 name =tom
    return "index";
}

测试:

在这里插入图片描述

6.后端返回json格式数据

在方法上添加@ResponseBody注解,将返回值转换成 json格式输出

@ResponseBody
@RequestMapping("/show")
public List<User> show(){
    ArrayList<User> list = new ArrayList<>();
    list.add(new User(1, "Tom", 19, "男", new Date()));
    list.add(new User(2, "Bob", 20, "男", new Date()));
    list.add(new User(3, "Kate", 17, "男", new Date()));
    return list;
}

测试:
在这里插入图片描述

四、转发与重定向

转发:

@RequestMapping("/index")
public String index(){
    return "index";
}

@RequestMapping("/forward")
public String forward(){
    //return "index";
    return "forward:/index";
    //return InternalResourceViewResolver.FORWARD_URL_PREFIX + "index";
}

在这里插入图片描述

重定向:

@RequestMapping("/index")
public String index(){
    return "index";
}

@RequestMapping("/redirect")
public String redirect(){
    return "redirect:/index";
    //return InternalResourceViewResolver.REDIRECT_URL_PREFIX + "index";
}

在这里插入图片描述

五、静态资源放行

1.配置Spring MVC默认处理器

<mvc:annotation-driven/>

存在缺陷(可能会与自定义404页面冲突)

2.配置静态资源映射(推荐)

<mvc:resources mapping="/css/**" location="/css/"/>
<mvc:resources mapping="/js/**" location="/js/"/>
<mvc:resources mapping="/fonts/**" location="/fonts/"/>
<mvc:resources mapping="/images/**" location="/images/"/>

六、使用alibaba的fastjson代替Spring MVC默认jackson

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.80</version>
</dependency>

配置fastjson

<mvc:annotation-driven>
    <mvc:message-converters>
        <!--使用fastjson取代默认jackson-->
        <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
            <property name="supportedMediaTypes">
                <list>
                    <value>application/json</value>
                </list>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>
@RequestMapping("/userList")
public String getUserList(@RequestBody Department department){
    for (User user :department.getUserList()) {
        System.out.println(user);
    }
    /*
    User(id=1, name=tom, age=18, gender=male, birthday=Sat Apr 09 09:36:00 CST 2022)
    User(id=2, name=marry, age=19, gender=female, birthday=Sat Apr 09 09:36:00 CST 2022)
    User(id=3, name=Bob, age=30, gender=male, birthday=Sat Apr 09 09:36:00 CST 2022)
    */
    return "index";
}

七、中文乱码处理

1.设置tomcat字符集

修改apache-tomcat-xxx\conf\server.xml文件

在这里插入图片描述

2.设置idea文件编码

在这里插入图片描述

3.配置全局过滤器

解决请求(前端向后端传递参数)中的中文乱码

<!--jdk1.6以后自动解决Get请求中的中文乱码问题-->
<!--CharacterEncodingFilter过滤器解决Post请求中的中文乱码问题-->
<filter>
    <filter-name>encode</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>encode</filter-name>
    <url-pattern>/</url-pattern>
</filter-mapping>

解决响应(后端向前端返回)中的中文乱码

@RequestMapping(produces = {"application/json;charset=UTF-8"})

测试:

@ResponseBody
@RequestMapping("/encode")
public String encode(){
    return "编码";
}

在这里插入图片描述

八、自定义全局异常处理

<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-mvc.xml</param-value>
    </init-param>
    <!--开启Spring MVC 404异常处理(默认关闭)-->
    <init-param>
        <param-name>throwExceptionIfNoHandlerFound</param-name>
        <param-value>true</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

@RestControllerAdvice = @ResponseBody + @ControllerAdvice

@ControllerAdvice 和 @ExceptionHandler(填入Throwable的子类异常) 一起使用,捕获该异常

import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

@RestControllerAdvice
public class GlobalExceptionHandler{

    //处理除零异常
    @ExceptionHandler(ArithmeticException.class)
    public String handleException(ArithmeticException ex) {
        //输出异常信息
        return ex.getMessage();
    }

    //处理404异常
    @ExceptionHandler(NoHandlerFoundException.class)
    public String handleNoHandlerFoundException(NoHandlerFoundException ex) {
        //输出异常信息
        return ex.getMessage();
    }
}
@Controller
//解决响应时的中文乱码问题
@RequestMapping(value = "/hello" ,produces = {"application/json;charset=UTF-8"})
public class HelloController {
    @ResponseBody
    @RequestMapping("/exception")
    public String exception(){
        int i = 1 / 0;
        return "编码";
    }
}

测试:

在这里插入图片描述在这里插入图片描述

注意:

如果全局异常处理器中直接返回中文会产生乱码,解决方法:

<mvc:annotation-driven>
    <mvc:message-converters>
        <!--解决全局异常处理器中返回中文乱码-->
        <bean class="org.springframework.http.converter.StringHttpMessageConverter">
            <property name="supportedMediaTypes">
                <list>
                    <value>text/plain;charset=UTF-8</value>
                    <value>text/html;charset=UTF-8</value>
                </list>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

配置web.xml:

<error-page>
    <!--状态码-->
    <error-code>404</error-code>
    <!--跳转页面-->
    <location>/html/404.html</location>
</error-page>

测试:

在这里插入图片描述

九、Restful接口风格

1、每一个URI代表1种资源

2、客户端使用GET、POST、PUT、DELETE4个表示操作方式的动词对服务端资源进行操作:GET用来获取资源,POST用来新建资源,PUT用来更新资源,DELETE用来删除资源

3、通过操作资源的表现形式来操作资源

4、资源的表现形式是XML或者HTML

动作普通CRUD的URLRestful的URLRestful对应的HTTP方法
查询Article?id=1Article/{id}GET
添加Article?title=xxx&body=xxxArticlePOST
修改Article/update?id=1Article/{id}PUT
删除Article/delete?id=1Article/{id}DELETE

十、跨域访问

在Controller类上添加注解@CrossOrigin注解

@CrossOrigin(value = "http://localhost:8080")
public class HelloController {
	...
}

十一、Spring MVC 拦截器

public class MyInterceptor1 implements HandlerInterceptor {

    //进入controller(处理器)之前执行(可以进行权限控制)
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }

    //controller(处理器)成功返回前执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        
    }

    //当preHandle()方法返回true并且controller(处理器)请求成功后执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        
    }
}

注册拦截器

<!--注册拦截器(未注册处理器映射器时)-->
<mvc:interceptors>
    <mvc:interceptor>
        <!--path指明拦截的url-->
        <mvc:mapping path="/**"/>
        <bean id="myInterceptor1" class="com.qingsongxyz.interceptor.MyInterceptor1"/>
    </mvc:interceptor>
</mvc:interceptors>

多个拦截器执行顺序

public class MyInterceptor1 implements HandlerInterceptor {

    //进入controller(处理器)之前执行
    //如果返回true会进入下一个拦截器或真正的处理器(controller里面的方法)
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("1.MyInterceptor1的preHandle()执行...");
        return true;
    }

    //controller(处理器)返回前执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("5.MyInterceptor1的postHandle()执行...");
    }

    //当preHandle()方法返回true和controller(处理器)请求成功后执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("7.MyInterceptor1的afterCompletion()执行...");
    }
}
public class MyInterceptor2 implements HandlerInterceptor {

    //进入controller(处理器)之前执行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("2.MyInterceptor2的preHandle()执行...");
        return true;
    }

    //controller(处理器)返回前执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("4.MyInterceptor2的postHandle()执行...");
    }

    //当preHandle()方法返回true和controller(处理器)请求成功后执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("6.MyInterceptor2的afterCompletion()执行...");
    }
}

Controller(处理器)

@Controller
//解决响应时的中文乱码问题
@RequestMapping(value = "/hello" ,produces = {"application/json;charset=UTF-8"})
public class HelloController {
    @ResponseBody
    @RequestMapping("/show")
    public List<User> show(){
        ArrayList<User> list = new ArrayList<>();
        list.add(new User(1, "Tom", 19, "男", new Date()));
        list.add(new User(2, "Bob", 20, "男", new Date()));
        list.add(new User(3, "Kate", 17, "男", new Date()));
        System.out.println("3.helloController里面的handler执行...");
        return list;
    }
}

/*
    1.MyInterceptor1的preHandle()执行...
    2.MyInterceptor2的preHandle()执行...
    3.helloController里面的handler执行...
    4.MyInterceptor2的postHandle()执行...
    5.MyInterceptor1的postHandle()执行...
    6.MyInterceptor2的afterCompletion()执行...
    7.MyInterceptor1的afterCompletion()执行...
*/

测试

在这里插入图片描述

十二、文件上传下载

导入依赖

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.3</version>
    <exclusions>
        <exclusion>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
</dependency>

配置Spring的文件解析器

<!--文件上传-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="10240000"/>
    <property name="defaultEncoding" value="utf-8"/>
</bean>
@RestController
@RequestMapping(value = "/file", produces = {"application/json;charset=UTF-8"})
public class FileController {

    @PostMapping("/upload")
    public String upload(MultipartFile file, HttpSession session) throws IOException {

        String path = session.getServletContext().getRealPath("/upload");
        File directory = new File(path);
        if(!directory.exists())
        {
            directory.mkdir();
        }
        String uuid = UUID.randomUUID().toString();
        String realFileName = file.getOriginalFilename();
        String extension = FilenameUtils.getExtension(realFileName);
        String storeFileName = uuid + "." + extension;
        File destFile = new File(path + "\\" + storeFileName);
        file.transferTo(destFile);
        return "文件" + realFileName + "上传成功!";
    }

    @GetMapping("/download")
    public String download(String filename, HttpServletResponse response, HttpSession session) throws IOException {

        //设置响应头,将文件以附件形式输出
        response.setHeader("content-disposition", "attachment;filename=" + filename);

        String realPath = session.getServletContext().getRealPath("/upload");

        String file = realPath + "\\" + filename;

        //以流的形式对外输出
        IOUtils.copy(new FileInputStream(file), response.getOutputStream());

        return "文件" + filename + "下载成功!";
    }
}

简单页面

上传:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Upload</title>
</head>
<body>
    <form action="/file/upload" method="post" enctype="multipart/form-data">
        file:<input type="file" name="file">
        <input type="submit" value="上传">
    </form>
</body>
</html>

下载:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>欢迎来到主页!</h1>
    <a href="/upload/1.png">下载</a>
    <a href="/file/download?filename=1.png">下载</a>
</body>
</html>

测试:

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

十三、Spring MVC执行流程

流程图:
在这里插入图片描述

详细流程:

1.浏览器发送请求到前端控制器,前端控制器获取request中的URI,交给处理器映射器进行处理

2.处理器映射器通过URI去找处理器(Controller中的方法)处理,如果没有找到处理器,会产生404异常(NoHandlerFoundException),如果找到了处理器,就返回一个处理器执行链(包括一系列的拦截器Interceptors和真正的处理器)给前端控制器

3.前端控制器将这个处理器执行链交给处理器适配器,处理器适配器先执行一系列的拦截器,然后执行处理器(Controller中的方法),最终返回一个ModelAndView对象给前端控制器

4.前端控制器将这个ModelAndView对象交给视图解析器,视图解析器将Model中的数据取出,封装到View对象中,再将这个View对象返回给前端控制器

5.最后前端控制器将View对象封装成Response对象返回给浏览器

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值