SpringMVC 学习记录

SpringMVC

什么是MVC
MVC是一种软件架构的思想,将软件按照模型、视图、控制器来划分
M: Model, 模型层,指工程中的avaBean,作用是处理数据
JavaBean分为两类:
一类称为实体类Bean:专存储业务数据的,如Student、 User等
一类称为业务处理Bean:指Service或Dao对象,专用于处理业务逻辑和数据访问。
V: View, 视图层,指工程中的html或jsp等页面,作用是与用户进行交互,展示数据
C: Controller, 控制层,指工程中的servlet,作用是接收请求和响应浏览器
MVC的工作流程:
用户通过视图层发送请求到服务器,在服务器中请求被Controller接收, Controller调用相应的Mode层处理请求,处理完毕将结果返回到Controller, Controller再根据请求处理的结果找到相应的View视图,渲染数据后最终响应给浏览器

什么是SpringMVC
SpringMVC是Spring的一个后续产品,是Spring的一个子项目
SpringMVC是Spring为表述层开发提供的一整套完备的解决方案。在表述层框架历经Strust、WebWork、Strust2等诸多产品的历代更迭之后,目前业界普遍选择了SpringMVC作为Java EE项目表述层开发的首选方案。
注: 三层架构分为表述层(或表示层)、业务逻辑层、数据访问层,表述层表示前台页面和后台servlet

SpringMVC的特点
Spring 家族原生产品,与IOC容器等基础设施无缝对接
基于原生的Servlet, 通过了功能强大的前端控制器DispatcherServlet,对请求和响应进行统一处理
表述层各细分领域需要解决的问题全方位覆盖,提供全面解决方案
代码清新简洁,大幅度提升开发效率
内部组件化程度高,可插拔式组件即插即用,想要什么功能配置相应组件即可
性能卓著,尤其适合现代大型、超大型互联网项目要求

开始

  • 创建maven工程

  • 打包方式为war

    <packaging>war</packaging>
    

  • 引入依赖
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.20</version>
</dependency>

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.11</version>
</dependency>

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

<!--    thymeleaf和spring5 整合包-->
<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring5</artifactId>
    <version>3.0.11.RELEASE</version>
</dependency>
  • 请求拦截

符合规则的请求都被Spring的DispatcherServlet处理

<servlet>
    <servlet-name>SpringMVC</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>SpringMVC</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

/与/*有区别,/无法处理.jsp,因为tomcat的web.xml配置过jsp的请求拦截,/ *可以拦截所有请求包括jsp

SpringMVC的配置文件默认的位置和名称:

位置: WEB- INF下

名称: <servlet - name>-servlet . xml,当前配置下的配置文件名为SpringMVC- servlet . xml

自定义配置文件位置、服务器启动时初始化DispatcherServlet

<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:SpringMVC-servlet.xml</param-value>
    </init-param>
<!--        DispatcherServlet load on startup 服务器启动时初始化-->
    <load-on-startup>1</load-on-startup>
</servlet>
...

创建配置文件SpringMVC- servlet . xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="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.learn"/>

<!--    thymeleaf视图解析器-->
    <bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
        <property name="order" value="1"/>
        <property name="characterEncoding" value="UTF-8"/>
        <property name="templateEngine">
            <bean class="org.thymeleaf.spring5.SpringTemplateEngine">
                <property name="templateResolver">
                    <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
                        <!--视图前缀-->
                        <property name="prefix" value="/templates/"/>
                        <!--视图后缀-->
                        <property name="suffix" value=".html"/>
                        <property name="templateMode" value="HTML5"/>
                        <property name="characterEncoding" value="UTF-8"/>
                    </bean>
                </property>
            </bean>
        </property>
    </bean>
</beans>

处理静态资源,例如html、js、 css、 jpg,若只设置该标签,则只能访问静态资源,其他请求则无法访问,此时必须设置<mvc:annotation-driven/>解决问题

<mvc:default-servlet-handler/>
<mvc:annotation-driven>
    <mvc:message-converters>
        <!--处理响应中文乱码-->
        <bean class="org.springframework.http.converter.StringHttpMessageConverter">
            <property name="defaultCharset" value="UTF-8"/>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

配置tomcat服务器

编写页面

web-app/templates/index.hml

编写HelloController

@Controller
public class HelloController {
    @RequestMapping("/")
    public String helloIndex(){
        // 返回逻辑视图
        return "index";
    }
}

运行服务器,访问tomcat配置的URL,如http://localhost:8080/

@RequestMapping

概要

  • 作用:将请求和处理请求的控制器方法关联起来,建立映射关系

  • SpringMVC接收到指定的请求,就会来找到在映射关系中对应的控制器方法来处理这个请求。

  • 注解位置:

    • 类: 设置映射请求的请求路径的初始信息
    • 法: 设置映射请求请求路径的具体信息
  • 参数,@RequestMapping("/test") <=> @RequestMapping(value = "/test")

public @interface RequestMapping{
    String name() default "";
    @AliasFor("path")
    String[] value() default {};
    @AliasFor("value")
    String[] path() default {};// 当前浏览器所发送请求的请求路径匹配value属性中的任何一个值则当前请求就会被注解所标识的方法进行处理
    RequestMethod[] method() default {};// 请求方式 GET POST PUT DELETE HEAD OPTIONS PATCH TRACE,匹配任意一种将被处理,他们也有自己的mapping如@GetMapping
    String[] params() default {};
    // "username":表示当前所匹配请求的请求参数中必须携带username参数
    // '!username": 表示当前所匹配请求的请求参数中一定不能携带username参数
    // "username=123":表示当前所匹配请求的请求参数中必须携带username参数且值必须为123
    // "username!=456":表示当前所匹配请求的请求参数中可以不携带username,若携带值一定不能是456
    String[] headers() default {};
    // "Referer":要求请求映射所匹配的请求必须携带Referer请求头信息
    // "!Referer":要求请求映射所匹配的请求必须不能携带Referer请求头信息
    // "Referer=aaa":要求请求映射所匹配的请求必须携带header请求头信息且Referer=aaa
    // "Referer!=bbb":要求请求映射所匹配的请求必须携带header请求头信息且Referer!=bbb
    String[] consumes() default {};
    String[] produces() default {};
}
@Controller
@RequestMapping("/main")
public class HelloController {
    @RequestMapping("/index")  // http://localhost:8080/main/index
    public String helloIndex(){
        return "index";
    }

    @RequestMapping("/hello")  // http://localhost:8080/main/hello
    public String toHello(){
        return "hello";
    }
}
@Controller
public class HelloController {
    @RequestMapping("/")  // http://localhost:8080/
    public String helloIndex(){
        return "index";
    }
    @RequestMapping("/hello")  // http://localhost:8080/hello
    public String toHello(){
        return "hello";
    }
}

为什么"/"可以匹配到 http://localhost:8080/ ?这与你配置tomcat服务器时设置的URL有关

若当前请求满足@RequestMapping注解的value和method属性,但是不满足headers属性,此时页面显示404错误,即资源未找到

Ant风格

  • ?:表示任意的单个字符
  • *:表示任意的0个或多个字符
  • **:表示任意层数的任意目录 如:/**/test 可以匹配到 /do/test/main/do/test
  • 注意:在使用**时,只能使用/**/xx的方式

RESTful

SpringMVC路径中的占位符常用于RESTful风格中,当请求路径中将某些数据通过路径的方式传输到服务器中,就可以在相应的@RequestMapping注解的value属性中通过占位符{xxx}表示传输的数据,在通过@PathVariable注解,将占位符所表示的数据赋值给控制器方法的形参

/back/user/{id}

@Controller
@RequestMapping("/back")
public class BackController {
    @RequestMapping("/user/{id}")  
    public String getUser(@PathVariable("id")Integer userId){// 路径拿到参数id的值赋值给userId
        return "info";
    }
    
    @RequestMapping("/user/{param1}/{param2}")
    public String action(@PathVariable("param1")Integer p1,@PathVariable("param2")Integer p2){
        return "action";
    }
}

@RequestBody

public @interface RequestBody {
    boolean required() default true;
}

可以获取请求体信息,使用@RequestBody注解标识控制器方法的形參,当前请求的请求体就会为当前注解所标识的形参赋值

示例

ajax

// ajax
$.ajax({
    url: "/test",
    method: "POST",
    contentType: "application/json;charset=UTF-8",
    data: JSON.stringify({// 需要JSON.stringify()......否则400.....
        name: "张三",
        gender: "123456",
        age: 18
    }), success: function (res) {
        console.log(res)
    }
})

axios

axios.post("/test",{
    name:"张三",
    gender: "男",
    age: 18
}).then(res => {
    console.log(res)
}).catch(err => {
    console.log(err)
})
@RequestMapping(value = "/test",method = RequestMethod.POST)
public void test(@RequestBody User user, HttpServletResponse response) throws IOException {
    System.out.println(user);
    response.getWriter().write(user.toString());
}

除此之外还可以使用Map接收...等等...

// ...
public void test(@RequestBody Map<String,String> map){
	//...
}

踩坑

415异常:Content type 'application/json;charset=UTF-8' not supported.....

02:51:15.601 [http-nio-8080-exec-7] WARN org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver - Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/json;charset=UTF-8' not supported]
02:51:15.602 [http-nio-8080-exec-7] DEBUG org.springframework.web.servlet.DispatcherServlet - Completed 415 UNSUPPORTED_MEDIA_TYPE

需要jackson依赖..........

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
  <groupId>com.fasterxml.jackson.datatype</groupId>
  <artifactId>jackson-datatype-jdk8</artifactId>
  <version>2.13.4</version>
</dependency>

@RequestHeader

针对请求头

public @interface RequestHeader {
    @AliasFor("name")
    String value() default "";
    @AliasFor("value")
    String name() default "";
    boolean required() default true;// 必须携带该请求头信息
    String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
}

将请求头信息和控制器方法的形参绑定

@RequestMapping("/test")
public String test(@RequestHeader("Referer")String referer){
    return "test";
}

@CookieValue

针对cookie

public @interface CookieValue {
    @AliasFor("name")
    String value() default "";
    @AliasFor("value")
    String name() default "";
    boolean required() default true;
    String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
}

将cookie数据和控制器方法的形参绑定

@RequestMapping("/aaa")
public String aaa(@CookieValue("JSESSIONID")String jSessionId){
    return "aaa";
}

@ResponseBody

public @interface ResponseBody {
}

使用

@RequestMapping(value = "/test")
@ResponseBody
public User test() {
    return new User("zhangsan","男",20);
}

请求:http://localhost:8080/test

响应:{"name":"zhangsan","gender":"男","age":20}

@RestController

@RestController注解是springMVC提供的一个复合注解,标识在控制器的类上,就相当于为类添加了@Controller注解,并且为其中的每个方法添加了@ResponseBody注解

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

获取请求参数

一、通过ServletAPI

@RequestMapping("/hello")
public String toHello(HttpServletRequest request){
    String username = request.getParameter("username");
    return "hello";
}

二、通过SpringMVC方式

参数名一致

http://xxx/user?username=123

@RequestMapping("/user")
public String getUser(String username){
    return "info";
}

参数名不一致

@RequestParam
public @interface RequestParam{
    @AliasFor("name")
    String value() default "";// 参数名
    @AliasFor("value")
    String name() default "";
    boolean required() default true;// 必须携带该参数 如@RequestParam(value = "id",required = true)  则请求时必须携带id参数
    String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";// 如:@RequestParam(value = "id", defaultValue = "123") 给id设置默认值为123
}

http://xxx/user?username=123

@RequestMapping("/user")
public String getUser(@RequestParam("username")String user){
    return "info";
}

参数是实体类

@RequestMapping("/user")
public String getUser(User user){
    return "info";
}

请求参数乱码

解决获取请求参数的乱码问题,可以使用SpringMVC提供的编码过滤器CharacterEncodingFilter, 但是必须在web.xml中进行注册

在HttpServletRequest获取请求参数之前设置编码,所以,编码过滤在web.xml必须在处理请求之前

示例

http://localhost:8080/hello?name=张三

@RequestMapping("/hello")
public String toHello(String name){
    System.out.println(name);// 此时输出:张三 ,乱码
    return "hello";
}

解决

web.xml

<filter>
    <filter-name>characterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

forceEncoding:连同响应编码也设置成UTF-8

域对象共享数据

Request

ServletAPI

使用ServletAPI向request域对象共享数据

@RequestMapping("/hello")
public String toHello(HttpServletRequest request){
    request.setAttribute("username", "123");
    return "hello";
}

ModelAndView

使用ModelAndView向request域对象共享数据

  • ModelAndView有Model和View的功能
    • Model:主要用于向请求域共享数据
    • View:主要用于设置视图,实现页面跳转
@RequestMapping("/hello")
public ModelAndView toHello(){
    ModelAndView mv = new ModelAndView();
    // 共享数据
    mv.addObject("msg","hello");
    // 页面跳转
    mv.setViewName("hello");
    return mv;
}
<h2 th:text="${msg}"></h2>

Model

使用Model向request域对象共享数据

@RequestMapping("/hello")
public String toHello(Model model){
    // 共享数据
    model.addAttribute("msg","hello");
    return "hello";
}
<h2 th:text="${msg}"></h2>

ModelMap

与Model类似

@RequestMapping("/hello")
public String toHello(ModelMap modelMap){
    // 共享数据
    modelMap.addAttribute("msg","hello");
    return "hello";
}
<h2 th:text="${msg}"></h2>

Map

@RequestMapping("/hello")
public String toHello(Map<String,Object> map){
    // 共享数据
    map.put("msg","hello");
    return "hello";
}
<h2 th:text="${msg}"></h2>

在底层中,这些类型的形参最终都是通过BindingAwareModelMap创建

Session

示例

@RequestMapping("/hello")
public String toHello(HttpSession session){
    session.setAttribute("msg", "hello session");
    return "hello";
}
 <h2 th:text="${session.msg}"></h2>

钝化与活化

勾上框框即可

Application

@RequestMapping("/hello")
public String toHello(HttpSession session){
    ServletContext servletContext = session.getServletContext();
    // 共享数据
    servletContext.setAttribute("msg", "hello application");
    return "hello";
}
 <h2 th:text="${application.msg}"></h2>

View

SpringMVC中的视图是View接口,视图的作用渲染数据,将模型Model中的数据展示给用户
SpringMVC视图的种类很多,默认有转发视图和重定向视图,当工程引入jstI的依赖,转发视图会自动转换为JstIView,若使用的视图技术为Thymeleaf,在SpringMVC的配置文件中配置 了Thymeleaf的视图解析器,由此视图解析器解析之后所得到的是ThymeleafView

SpringMVC视图

SpringMVC中的视图是View接口,视图的作用渲染数据,将模型Model中的数据展示给用户,SpringMVC视图的种类很多,默认有转发视图和重定向视图,当工程引入jstl的依赖,转发视图会自动转换为]stIView,若使用的视图技术为Thymeleaf,在SpringMVC的配置 文件中配置了Thymeleaf的视图解析器,由此视图解析器解析之后所得到的是ThymeleafView

thymeleafView

浏览器发送请求,若请求地址符合前端控制器的url-pattern, 该请求就会被前端控制器DispatcherServlet处理。前端控制器会读取SpringMVC的核心配置文件,通过扫描组件找到控制器,将请求地址和控制器中@RequestMapping注解的value属性值进行匹配,若匹配成功,该注解所标识的控制器方法就是处理请求的方法。处理请求的方法需要返回-个字符串类型的视图名称,该视图名称会被视图解析器解析,加上前缀和后缀组成视图的路径,通过Thymeleaf对视图进行渲染,最终转发到视图所对应页面

转发并渲染

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

InternalResourceView

@RequestMapping("/hello")
public String toHello(){
    return "forward:/main/hello";// 转发
}

如果是JSP,需要配置该视图解析器

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"></bean>

RedirectView

重定向

@RequestMapping("/hello")
public String toHello(){
    return "redirect:/main/hello";
}

视图控制器

视图控制器:为当前的请求直接设置视图名称实现页面跳转
若设置视图控制器,则只有视图控制器所设置的请求会被处理,其他的请求将全部404
此时必须在配置一个标签: <mvc :annotation -driven />

<mvc:annotation-driven/>
<mvc:view-controller path="/" view-name="index"/>

RESTful

Representational State Transfer,表现层资源状态转移。

<!--    请求方式过滤,可以使用put和delete-->
<filter>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

以post请求发送,并携带_method参数过去,如果以ajax发送请求,不需要携带_method参数

<a th:href="@{/user}">获取所有用户信息</a>
<a th:href="@{/user/1}">根据ID获取用户信息</a>

<form th:action="@{/user}" method="post">
    <input type="submit" value="添加用户信息"/>
</form>

<form th:action="@{/user}" method="post">
    <input name="_method" type="hidden" value="delete"/>
    <input type="submit" value="删除用户信息"/>
</form>

<form th:action="@{/user}" method="post">
    <input name="_method" type="hidden" value="put"/>
    <input type="submit" value="修改用户信息"/>
</form>
// 查询所有
@RequestMapping("/user")
public String getUsers(){
    return "success";
}
// 查询单个
@RequestMapping("/user/{id}")
public String getUserById(){
    return "success";
}
// 添加
@RequestMapping("/user")
public String addUser(){
    return "success";
}
// 删除
@RequestMapping(value = "/user",method = RequestMethod.DELETE)
public String deleteUser(){
    return "success";
}
// 修改
@RequestMapping(value = "/user",method = RequestMethod.PUT)
public String updateUser(){
    return "success";
}

处理静态资源

<!--处理静态资源,例如html、js、 css、 jpg,若只设置该标签,则只能访问静态资源,其他请求则无法访问,此时必须设置<mvc:default-servlet-handler/>解决问题-->
<mvc:default-servlet-handler/>

文件上传/下载

ResponseEntity用于控制器方法的返回值类型,该控制器方法的返回值就是响应到浏览器的响应报文使用ResponseEntity实现下载文件的功能

前置

<!--    文件上传解析器 通过id获取bean,必须设置id为multipartResolver-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
<dependency>
  <groupId>commons-fileupload</groupId>
  <artifactId>commons-fileupload</artifactId>
  <version>1.3.3</version>
</dependency>

下载

@RequestMapping(value = "/test/download")
public ResponseEntity<byte[]> testDownload(HttpSession session) throws IOException {
    ServletContext servletContext = session.getServletContext();
    // 普通项目是out目录   maven是target目录(war包)
    String realPath = servletContext.getRealPath("static" + File.separator + "img");// E:\xxx\learnSpringMvc\target\learnSpringMvc\static\img
    realPath = realPath + File.separator + "test.jpg";
    // 创建流
    FileInputStream inputStream = new FileInputStream(realPath);
    // 字节
    byte[] bytes = new byte[inputStream.available()];// 字节数
    // 流 -->> 字节
    inputStream.read(bytes);
    MultiValueMap<String,String> headers = new HttpHeaders();// 响应头
    // 设置狭隘方式Content-Disposition以附件attachment方式下载
    headers.add("Content-Disposition", "attachment;filename=test.jpg");
    
    HttpStatus statusCode = HttpStatus.OK;// 响应状态码
    // 响应实体对象
    return new ResponseEntity<>(bytes, headers, statusCode);
}

上传

@RequestMapping("/test/upload")
@ResponseBody
public String upload(MultipartFile file, HttpSession session) throws IOException {
    if (file == null) {
        return "获取目标文件失败";
    }
    // 原始文件名
    String originalFilename = file.getOriginalFilename();
    if (originalFilename == null){
        return "获取文件名失败";
    }
    // 截取后缀
    System.out.println(originalFilename);
    if (!originalFilename.contains(".")){
        return "获取文件后缀失败";
    }
    String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
    // 上下文
    ServletContext servletContext = session.getServletContext();
    // 真实路径
    String uploadPath = servletContext.getRealPath("upload");
    // 目录是否存在
    File f = new File(uploadPath);
    if (!f.exists()){
        if (!f.mkdir()) {
            // 创建目录失败
            return "error";
        }
    }
    // 上传文件名
    String finalPath = uploadPath + File.separator + UUID.randomUUID() + suffix;
    System.out.println("上传文件位置:" + finalPath);
    // 上传
    file.transferTo(new File(finalPath));

    return "success";
}

⚠️ 注意前端input的name要与MultipartFile的变量名一致

拦截器

示例

SpringMVC中的拦截器用于拦截控制器方法的执行
SpringMVC中的拦截器需要实现HandlerInterceptor
SpringMVC的拦截器必须在SpringMVC的配置文件中进行配置:

@Component
public class HelloInterceptor implements HandlerInterceptor {
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("渲染完视图之后执行...");
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("控制器方法之后执行...");
    }
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("控制器方法之前执行...");
        return true;// false表示被拦截,不放行
    }
}
<mvc:interceptors>
<!--    bean和ref标签所配置的拦截器默认对DispatcherServlet处理的所有的请求进行拦截-->
<!--        <ref bean="helloInterceptor"/>-->
<!--        精确拦截请求 处理与不处理的路径    /*表示一个层级,并不是所有请求,/**表示所有请求  -->
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <mvc:exclude-mapping path="/bbb"/>
        <ref bean="helloInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

拦截顺序

@Component
public class HelloInterceptor implements HandlerInterceptor {
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("HelloInterceptor afterCompletion...");
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("HelloInterceptor postHandle...");
    }
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("HelloInterceptor preHandle...");
        return false;
    }
}
@Component
public class SecondInterceptor implements HandlerInterceptor {
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("SecondInterceptor afterCompletion...");
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("SecondInterceptor postHandle...");
    }
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("SecondInterceptor preHandle...");
        return true;
    }
}

helloInterceptor preHandle -->> true

secondInterceptor preHandle -->> true

👇

HelloInterceptor preHandle...
SecondInterceptor preHandle...
SecondInterceptor postHandle...
HelloInterceptor postHandle...
SecondInterceptor afterCompletion...
HelloInterceptor afterCompletion...

helloInterceptor preHandle -->> true

secondInterceptor preHandle -->> false

👇

HelloInterceptor preHandle...
SecondInterceptor preHandle...
HelloInterceptor afterCompletion...

helloInterceptor preHandle -->> false

secondInterceptor preHandle -->> true

👇

HelloInterceptor preHandle...

异常处理解析器

xml方式

控制器出现异常时

public interface HandlerExceptionResolver {
    @Nullable
    ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}
<!--    异常映射-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="exceptionMappings">
        <props>
 <!--      遇到NullPointerException异常时,跳转到error视图-->
        <prop key="java.lang.NullPointerException">error</prop>
        </props>
    </property>
<!--        设置共享在请求域中的异常信息的属性名-->
    <property name="exceptionAttribute" value="ex"/>
</bean>
<h2 th:text="@{ex}"></h2>

注解方式

@ControllerAdvice

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {
    @AliasFor("basePackages")
    String[] value() default {};
    @AliasFor("value")
    String[] basePackages() default {};
    Class<?>[] basePackageClasses() default {};
    Class<?>[] assignableTypes() default {};
    Class<? extends Annotation>[] annotations() default {};
}

@ExceptionHandler

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExceptionHandler {
    Class<? extends Throwable>[] value() default {};
}
@ControllerAdvice
public class ExceptionController {
    // 哪些异常会被处理
    @ExceptionHandler(value = {NullPointerException.class,ClassNotFoundException.class})
    public String handlerException(Throwable ex, Model model){
        // 共享异常信息
        model.addAttribute("ex",ex);
        return "error";
    }
}

注解配置SpringMVC

在Servlet3.0环境中,容器会在类路径中查找实现javax.servlet.ServletContainerlnitializer接口的类, 如果找到的
话就用它来配置Servlet容器。
Spring提供了这个接口的实现,名为SpringServletContainerlnitializer, 这个类反过来又会查找实现
WebApplicationlnitializer的类并将配置的任务交给它们来完成。Spring3.2引入 了一个便利的
WebApplicationlnitializer基础实现,名为AbstractAnnotationConfigDispatcherServletlnitializer, 当我们的类
扩展了AbstractAnnotationConfigDispatcherServletlnitializer并将其部署到Servlet3.0容器的时候,容器会自动
发现它,并用它来配置Servlet上下文。

// 代替web.xml
public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    // 代替Spring配置文件
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{SpringConfig.class};
    }

    @Override
    // 代替SpringMVC配置文件
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{WebConfig.class};
    }

    @Override
    // 设置SpringMVC的前端控制器的DispatcherServlet的url-pattern
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    @Override
    // 设置过滤器
    protected Filter[] getServletFilters() {
        // 编码过滤器
        CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
        encodingFilter.setEncoding("UTF-8");
        encodingFilter.setForceEncoding(true);
        // 请求方式过滤器
        HiddenHttpMethodFilter httpMethodFilter = new HiddenHttpMethodFilter();

        return new Filter[]{httpMethodFilter,encodingFilter};
    }
}
@Configuration
public class SpringConfig {
}
@Configuration// 声明配置文件
@ComponentScan("com.learn")//扫描组件
@EnableWebMvc//开启注解驱动
public class WebConfig implements WebMvcConfigurer {
    @Override
    // 默认servlet处理静态资源
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
    @Override
    // 配置视图解析器
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
    }
    @Bean
    // 文件上传解析器
    public CommonsMultipartResolver multipartResolver(){
        return new CommonsMultipartResolver();
    }
    @Override
    // 拦截器
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new HelloInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/aaa");
    }
    @Override
    // 异常解析器
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
        SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();
        Properties prop = new Properties();
        prop.setProperty("java.lang.NullPointerException", "error");
        exceptionResolver.setExceptionMappings(prop);
        exceptionResolver.setExceptionAttribute("ex");
        resolvers.add(exceptionResolver);
    }
    @Bean
    // 配置生成模板解析器
    public ITemplateResolver iTemplateResolver(){
        WebApplicationContext context = ContextLoader.getCurrentWebApplicationContext();
        ServletContextTemplateResolver templateResolver =
                new ServletContextTemplateResolver(context.getServletContext());
        templateResolver.setPrefix("/templates/");
        templateResolver.setSuffix(".html");
        templateResolver.setCharacterEncoding("UTF-8");
        templateResolver.setTemplateMode(TemplateMode.HTML);
        return templateResolver;
    }
    @Bean
    public SpringTemplateEngine springTemplateEngine(ITemplateResolver iTemplateResolver){
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(iTemplateResolver);
        return templateEngine;
    }
    @Bean
    // 生成视图解析器并注入模板引擎
    public ViewResolver viewResolver(SpringTemplateEngine springTemplateEngine){
        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
        viewResolver.setCharacterEncoding("UTF-8");
        viewResolver.setTemplateEngine(springTemplateEngine);
        return viewResolver;
    }
}

learn from https://www.bilibili.com/video/BV1Ya411S7aT

posted @ 2022-11-17 22:24  夏末秋初~  阅读(54)  评论(0编辑  收藏  举报