后端学习 - SpringMVC

文章目录


一 SpringMVC 简介

1 MVC

  • Model 模型层:工程中的 JavaBean,包含实体 Bean(Customer、Student…) 和 业务处理 Bean(Service、DAO)
  • View 视图层:指工程中的 html 或 jsp 等页面,与用户进行交互,展示数据
  • Controller 控制层:指工程中的 servlet,作用是接收请求和响应浏览器
  • MVC 的工作流程 View <—> Controller <—> Model :用户通过视图层发送请求到服务器,在服务器中请求被Controller接收,Controller 调用相应的 Model 层处理请求,处理完毕将结果返回到 Controller,Controller 再根据请求处理的结果找到相应的 View 视图,渲染数据后最终响应给浏览器

2 SpringMVC

  • 是 Spring 的子项目
  • 主要作用是在 Spring 项目中进行表述层开发(是三层架构中的概念,三层架构:表述层(或表示层)、业务逻辑层、数据访问层,表述层表示前台页面和后台 servlet

3 创建第一个 SpringMVC 项目

目录结构如下
在这里插入图片描述

  1. 配置 maven 依赖,这里遇到了一个奇怪的版本问题
    <dependencies>
        <!-- SpringMVC -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.9</version>
        </dependency>

        <!-- 日志 -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>

        <!-- ServletAPI -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>

        <!-- Spring5和Thymeleaf整合包 -->
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
            <version>3.0.11.RELEASE</version>
        </dependency>
    </dependencies>
  1. 配置 web.xml 的 Servlet 及 mapping
    <!--
        注册springMVC的前端控制器,对浏览器所发送的请求统一进行处理
        在此配置下,springMVC的配置文件具有默认的位置和名称
        默认的位置:WEB-INF
        默认的名称:<servlet-name>-servlet.xml
        若要为springMVC的配置文件设置自定义的位置和名称(推荐)
        需要在servlet标签中添加init-param
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springMVC.xml</param-value>
        </init-param>
    -->
    <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.xml</param-value>  <!--对应resources目录下的文件-->
        </init-param>
        <load-on-startup>1</load-on-startup>  <!--服务器启动时初始化DispatcherServlet-->
    </servlet>
    <servlet-mapping>
        <servlet-name>springMVC</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
  1. 在 resources 目录下,配置 SpringMVC 的 xml:开启组件扫描、加入 thymeleaf 的名称空间并配置视图解析器(Thymeleaf 是服务器端的模板引擎,在服务器端获取模板和数据,生成结果输出给浏览器呈现结果)
<?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 https://www.springframework.org/schema/context/spring-context.xsd">

    <!--开启组件扫描-->
    <context:component-scan base-package="com.atguigu.mvc.controller"></context:component-scan>

    <!-- 配置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="/WEB-INF/templates/"/>
                        <!-- 视图后缀 -->
                        <property name="suffix" value=".html"/>
                        <property name="templateMode" value="HTML5"/>
                        <property name="characterEncoding" value="UTF-8" />
                    </bean>
                </property>
            </bean>
        </property>
    </bean>
</beans>
  1. 定义 Controller 类,并用注解标注类和方法;在 templates 中定义 index.html, target.html
@Controller
public class HelloController {
    /* 
    	通过@RequestMapping注解,可以通过请求路径匹配要处理的具体的请求
       /表示的当前工程的上下文路径 http://localhost:8080/project_context/
       返回字符串即可,thymeleaf自动为返回值配置前后缀
       方法名可以随意,根据注解进行解析
    */
    @RequestMapping("/")
    public String index(){
        return "index";
    }

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

总结:

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

二 @RequestMapping

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

1 注解类与方法的区别

  • @RequestMapping标识一个类:设置映射请求的请求路径的初始信息
  • @RequestMapping标识一个方法:设置映射请求请求路径的具体信息
@Controller
@RequestMapping("/test")
public class RequestMappingController {
	//此时请求映射所映射的请求的请求路径为:/test/testRequestMapping
	@RequestMapping("/testRequestMapping")
	public String testRequestMapping(){
		return "success";
	}
}

2 value 属性

  • 通过请求的请求地址匹配请求映射
  • 是一个字符串类型的数组,和其中任意字符串匹配则选择该方法,满足其中一个即可
  • @RequestMapping 必须设置的属性
<!--以下均能匹配 testRequestMapping()-->
<a th:href="@{/testRequestMapping}"> value1 </a>
<a th:href="@{/test}"> value2 </a>


@RequestMapping(
value = {"/testRequestMapping", "/test"}
)
public String testRequestMapping(){
	return "success";
}

3 method 属性

  • 根据请求方法 GET / POST / PUT / DELETE匹配请求映射
  • 默认任意请求方式均可匹配
  • RequestMethod 枚举类型的数组,表示该请求映射能够匹配多种请求方式的请求,满足其中一个即可
  • @RequestMapping(value="/idx", method={RequestMethod.GET}) 等于 @GetMapping(value="/idx"),其它方式同理
  • 如果 value 匹配而 method 不匹配,出现405错误

4 params 属性

  • 根据请求携带的的参数匹配请求映射
  • 是一个字符串类型的数组,需要同时满足所有条件,可以通过四种表达式设置请求参数和请求映射的匹配关系:
    “param”:要求请求映射所匹配的请求必须携带 param 请求参数
    “!param”:要求请求映射所匹配的请求必须不能携带 param 请求参数
    “param=value”:要求请求映射所匹配的请求必须携带 param 请求参数且 param=value
    “param!=value”:要求请求映射所匹配的请求必须携带 param 请求参数但 param!=value
<a th:href="@{/test(username='admin',password=123456)">测试</a>

@RequestMapping(
	value = {"/testRequestMapping", "/test"}
	,method = {RequestMethod.GET, RequestMethod.POST}
	,params = {"username","password!=123456"}
)
public String testRequestMapping(){
	return "success";
}

5 headers 属性

  • 根据请求的请求头信息匹配请求映射
  • 类似 params 属性,是一个字符串类型的数组,需要同时满足所有条件,可以通过四种表达式设置请求参数和请求映射的匹配关系:
    “header”:要求请求映射所匹配的请求必须携带 header 请求头信息
    “!header”:要求请求映射所匹配的请求必须不能携带 header 请求头信息
    “header=value”:要求请求映射所匹配的请求必须携带 header 请求头信息且 header=value
    “header!=value”:要求请求映射所匹配的请求必须携带 header 请求头信息且 header!=value

6 SpringMVC 支持路径中的占位符

  • 将请求携带的参数以请求路径的形式,向服务器传递参数
  • 如果 @RequestMappingvalue 属性中有路径的占位符,则请求时必须有占位符对应的参数,否则出现404
<a th:href="@{/goods/123}">访问goods,参数123</a>

@Controller
public class MyController {
    @RequestMapping("/goods/{id}")
    public String goodsPage(@PathVariable("id") int param_id) {
        System.out.println(param_id);
        return "goods";
    }
}

三 获取 Request 的一系列参数

1 通过控制器方法的形参

  • 将形参名和 Request 参数名对应即可(如果不同名,则需要使用 @RequestParam),如此只能获取 Request 参数,而不能获得请求头信息等
  • 如果有多个同名的参数,则将形参类型设置为字符串数组
<a th:href="@{/testParam(username='admin',password=123456)}">测试</a>

@RequestMapping("/testParam")
public String testParam(String username, String password){
	System.out.println("username:"+username+",password:"+password);
	return "success";
}

2 控制器方法形参 映射 Request 参数 - @RequestParam

  • value:Request 中的参数的 name 属性
  • required:标明该参数是否必须,如果是必须且未设置 defaultValue,则必须由 Request 传入
  • defaultValue:默认值
@Controller
public class MyController {
    @RequestMapping("/goods/diff")
    // request 传递参数为 name, passwd
    public String diff(
            @RequestParam(value = "name") String n,
            @RequestParam(value = "passwd", required = false) String pwd) {
        System.out.println(n + pwd);
        return "goods";
    }
}

3 控制器方法形参 映射 Request 请求头 - @RequestHeader

  • 同样具有 value, required, defaultValue,用法相同

4 控制器方法形参 映射 cookie数据 - @CookieValue

  • 同样具有 value, required, defaultValue,用法相同

5 通过 POJO 获取 Request 参数

将控制器方法形参设置为实体类类型,此时若浏览器传输的请求参数的参数名,和实体类中的属性名一致,那么请求参数就会为此属性赋值

<form th:action="@{/testpojo}" method="post">
	用户名:<input type="text" name="username"><br>
	密码:<input type="password" name="password"><br>
	性别:<input type="radio" name="sex" value=""><input type="radio"
		name="sex" value=""><br>
	年龄:<input type="text" name="age"><br>
	邮箱:<input type="text" name="email"><br>
	<input type="submit">
</form>
@RequestMapping("/testpojo")
public String testPOJO(User user){  // 根据request参数创建对象,前提是属性名和请求参数名相同
	System.out.println(user);  
	return "success";
}

6 解决获取参数的乱码问题

  • GET 请求的乱码问题可以通过更改 Tomcat 的配置文件 server.xml 解决
  • POST 请求的乱码问题必须在请求参数获取之前设置编码,要比 Servlet 启动更早
  • 服务器启动时,初始化顺序是 Listener -> Filter -> Servlet,可以使用 Filter 设置编码格式(不使用 Listener 是因为监听器只执行一次,负责初始化 / 销毁的动作,而 Filter 可以过滤所有符合路径请求),在 web.xml 中配置,并设置为首个 Filter
<!--配置springMVC的编码过滤器-->
<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>forceResponseEncoding</param-name>
		<param-value>true</param-value>
	</init-param>
</filter>

<filter-mapping>
	<filter-name>CharacterEncodingFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

四 域对象共享数据

域对象按范围从小到大分为:Request、Session、Application(ServletContext)

1 使用 ServletAPI 向 request 域对象共享数据(不建议)

@Controller
public class TestController {
    @RequestMapping("/servlet")
    public String m1(HttpServletRequest httpServletRequest) {
        httpServletRequest.setAttribute("k", "v");
        return "index";
    }
}
index.html:
<p th:text="${k}"></p>

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

  • 向 request 域对象共享数据的所有方式,本质是对使用 ModelAndView 进行共享的一种封装
  • 方法的返回值必须是 ModelAndView 类型
@Controller
public class TestController {
    @RequestMapping("/modelandview")
    public ModelAndView m2() {
        ModelAndView modelAndView = new ModelAndView();
        // 设置键值对
        modelAndView.addObject("k", "v");
        // 设置视图名称
        modelAndView.setViewName("index");

        return modelAndView;
    }
}

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

  • 类似于 ServletAPI
@Controller
public class TestController {
    @RequestMapping("/model")
    public String m3(Model model) {
        model.addAttribute("k", "v");
        return "index";
    }
}

4 使用 Map 向 request 域对象共享数据

@Controller
public class TestController {
    @RequestMapping("/map")
    public String m4(Map<String, Object> map) {
        map.put("k", "v");
        return "index";
    }
}

5 使用 ModelMap 向 request 域对象共享数据

  • 类似于 ServletAPI
@Controller
public class TestController {
    @RequestMapping("/modelmap")
    public String m5(ModelMap modelMap) {
        modelMap.addAttribute("k", "v");
        return "index";
    }
}

6 使用 ServletAPI 向 Session 域对象共享数据

@RequestMapping("/testSession")
public String testSession(HttpSession session){
	session.setAttribute("testSessionScope", "hello,session");
	return "index";
}

7 使用 ServletAPI 向 Application 域对象共享数据

@RequestMapping("/testApplication")
public String testApplication(HttpSession session){
	ServletContext application = session.getServletContext();
	application.setAttribute("testApplicationScope", "hello,application");
	return "index";
}

五 SpringMVC 视图

  • 默认的有:转发视图、重定向视图

1 Thymeleaf 视图

  • 实现了转发视图的功能
  • 当控制器方法中所设置的视图名称没有任何前缀时,此时的视图名称会被 SpringMVC 配置文件中所配置的视图解析器解析,视图名称拼接视图前缀和视图后缀所得到的最终路径,会通过转发的方式实现跳转
@RequestMapping("/testHello")
public String testHello(){
	return "hello";  // 没有任何前缀
}

Spring 配置文件中的视图解析器:

    <!-- 配置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>

2 转发视图

  • SpringMVC中默认的转发视图是 InternalResourceView
  • 当控制器方法中所设置的视图名称以"forward:"为前缀时,创建 InternalResourceView 视图,此时的视图名称不会被 SpringMVC 配置文件中所配置的视图解析器解析,而是会将前缀"forward:"去掉,剩余部分作为最终路径通过转发的方式实现跳转
@RequestMapping("/testForward")
public String testForward(){
	return "forward:/testHello";  // 先创建 InternalResourceView 再创建 Thymeleaf View
}

3 重定向视图

  • SpringMVC 中默认的重定向视图是 RedirectView
  • 当控制器方法中所设置的视图名称以"redirect:"为前缀时,创建 RedirectView 视图,此时的视图名称不会被 SpringMVC 配置文件中所配置的视图解析器解析,而是会将前缀"redirect:"去掉,剩余部分作为最终路径通过重定向的方式实现跳转
  • 重定向视图在解析时,会先将 redirect: 前缀去掉,然后会判断剩余部分是否以 / 开头,若是,则自动拼接上下文路径
@RequestMapping("/testRedirect")
public String testRedirect(){
	return "redirect:/testHello";
}

4 使用 view-controller 代替 控制器方法

  • 当控制器方法中,仅仅用来实现页面跳转,即只需要设置视图名称时,可以将处理器方法用 view-controller 标签表示
  • 当SpringMVC 中设置任何一个 view-controller 时,其他控制器中的请求映射将全部失效,可以使用标签<mvc:annotation-driven />开启
<mvc:view-controller path="/testView" view-name="success"></mvc:view-controller>

六 RESTful

  • Representational State Transfer,表现层资源状态转移
  • REST 风格提倡 URL 地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方式携带请求参数,而是将要发送给服务器的数据作为 URL 地址的一部分,以保证整体风格的一致性
  • 对于 HTTP 的四种请求:
操作 HTTP 请求RESTful
插入POST/user(请求为 POST)
删除DELETE/user/id(请求为 DELETE)
更新PUT/user(请求为 PUT)
查询GET/user/id(请求为 GET)
  • SpringMVC 中提供了两个过滤器:CharacterEncodingFilterHiddenHttpMethodFilter
  • 使用 HiddenHttpMethodFilter 模拟 DELETE 和 PUT
  • 在 web.xml 中注册时,必须先注册 CharacterEncodingFilter,再注册 HiddenHttpMethodFilter,因为CharacterEncodingFilter要求前面不能有任何获取请求参数的操作,而 CharacterEncodingFilter 获取了 request 参数

七 HttpMessageConverter

  • 报文信息转换器,将 请求报文 转换为Java对象,或将Java对象转换为 响应报文
  • HttpMessageConverter提供了两个注解和两个类型:@RequestBody,@ResponseBody,RequestEntity,ResponseEntity

1 @RequestBody

  • @RequestBody 可以获取请求体(仅仅是请求体而非整个 HTTP 报文,POST 具有请求体,而 GET 不具有
  • 需要在控制器方法设置一个形参,使用 @RequestBody 进行标识,当前请求的请求体就会为当前注解所标识的形参赋值
  • 可以将前端传来的 json 转为对象
@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestBody String requestBody){
	System.out.println(requestBody);
	return "success";
}

// 输出 requestBody:username=admin&password=123456
@Override
@PostMapping("/add")
public CmsPageResult add(@RequestBody CmsPage cmsPage) {  // @RequestBody将json转为对象
    return this.pageService.add(cmsPage);
}

2 RequestEntity

  • RequestEntity 封装 HTTP 请求报文,需要在控制器方法的形参中的 泛型位置 设置要将报文转换的目标类型,当前请求的请求报文就会赋值给该形参
  • 通过 getHeaders() 获取请求头信息,通过 getBody() 获取请求体信息
@RequestMapping("/request_entity")
public String requestEntityTest(RequestEntity<String> entity) {
    System.out.println(entity.getHeaders());
    System.out.println(entity.getBody());
    return "success";
}

3 @ResponseBody

  • @ResponseBody 用于标识一个 控制器方法,可以将该方法的 返回值 直接作为响应报文的 响应体 响应到浏览器
@RequestMapping("/response_body")
@ResponseBody
public String responseBodyTest() {
     return "TIGER YEAR";
}
  • 如果需要以 json 格式,打印 Java对象的响应体,需要导入 jackson 依赖并开启 mvc 注解驱动
pom.xml:
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.12.1</version>
        </dependency>

mvc核心配置文件:
<mvc:annotation-driven />
@RequestMapping("/response_body_json")
@ResponseBody
public User responseBodyJsonTest() {
    return new User("my_name", "my_passwd");
}

4 ResponseEntity

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

5 RestController

  • 复合注解,标识控制器类,就相当于为类添加了 @Controller 注解,并且为其中的每个方法添加了 @ResponseBody 注解

八 拦截器

  • 拦截的是控制器方法执行
  • 和过滤器 filter 的主要区别是,filter 作用于浏览器到 Servlet 的过程中,而拦截器作用于 Controller 执行前(Servlet 到 Controller 的过程中),以及 Controller 返回后 的过程中

1 配置方法

在 SpringMVC 配置文件中:

    <!-- 前两种对DispatcherServlet所处理的*所有的请求*进行拦截 -->
    <mvc:interceptors>
        <!--方式一:-->
        <bean class="interceptor.MyInterceptor"></bean>

        <!--方式二:-->
        <ref bean="myInterceptor"></ref>

        <!--方式三:可以自定义拦截路径-->
        <!--可以通过ref或bean标签设置拦截器,通过mvc:mapping设置需要拦截的请求,
        通过mvc:exclude-mapping设置需要排除的请求,即不需要拦截的请求-->
                <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <mvc:exclude-mapping path="/testRequestEntity"/>
            <ref bean="myInterceptor"></ref>
        </mvc:interceptor>

    </mvc:interceptors>

拦截器类:

@Component
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("pre");
        return true;  // 返回 true 放行
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("post");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("after");
    }
}

2 HandlerInterceptor 接口的三个方法

  • preHandle:返回值为 bool 类型,返回 true 放行,false 拦截
  • postHandle:控制器方法执行之后执行
  • afterComplation:渲染视图完毕之后执行

3 多个拦截器的执行顺序

  • 如果所有拦截器的 preHandle() 都返回 true,则 preHandle() 按照配置的顺序执行,postHandle()afterComplation() 按照配置的反序执行
  • 如果某个拦截器的 preHandle() 返回 false,preHandle() 返回 false 的拦截器和它之前的拦截器的 preHandle() 都会执行,postHandle() 都不执行,返回 false 的拦截器之前的拦截器的 afterComplation() 会倒序执行
  • 下图是所有拦截器的 preHandle() 都返回 true 的情况,如果拦截器3返回 false,则方法的执行顺序是: 1.prehandle->2.prehandle->3.prehandle->2.afterCompletion->1.afterCompletion
    在这里插入图片描述

九 异常处理器

1 基于配置

在 SpringMVC 的核心配置文件中:

  1. 设置异常类型与视图名称的映射
  2. 保存异常信息到 request 域(可选)
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="exceptionMappings">  <!--properties类型,键值对的结构-->
        <props>
        	<!--1. key表示处理器方法执行过程中出现的异常,全类名
        		value(写在双标签之内)表示若出现指定异常时,设置一个新的视图名称,跳转到指定页面-->
            <prop key="java.lang.ArithmeticException">error</prop>
        </props>
    </property>
    
    <!--2. exceptionAttribute:将出现的异常信息在请求域中进行共享,value属性是信息的key-->
    <property name="exceptionAttribute" value="info_key"></property>
</bean>

出现异常后,跳转到 error.html 访问异常信息:

<body>
    <p th:text="${info_key}"></p>
</body>

2 基于注解

  • 使用 @ControllerAdvice 注解异常处理类
  • 使用 @ExceptionHandler(异常类的class对象) 注解异常处理方法
// 标识为异常处理组件
@ControllerAdvice
public class AnnotationExceptionController {
    // 处理的异常类型,处理多种异常时 @ExceptionHandler(value = {...})
    @ExceptionHandler(ArithmeticException.class)
    public String divide0(Exception exception, Model model) {
        // 向 request 域中写入键值对
        model.addAttribute("info_key", exception);
        // 交由 thymeleaf 解析
        return "fail";
    }
}

十 使用注解配置 SpringMVC

  • 目的是使用配置类和注解代替 web.xml 和 SpringMVC 配置文件的功能

1 初始化类:代替 web.xml

  • 继承自 AbstractAnnotationConfigDispatcherServletInitializer
/*
* 需要完成的功能:
* 1.servlet
* 2.servlet-mapping
* 3.过滤器
* 4.Spring配置类
* 5.SpringMVC配置类*/
public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {

    // 指定 Spring 配置类
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{MySpringConfig.class};  // 创建长度为1的Class数组,放入MySpringConfig.class
    }

    // 指定SpringMVC的配置类
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{MyWebConfig.class};  // 同上
    }

    // 指定DispatcherServlet的映射规则,即url-pattern
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};  // 设置servlet-mapping
    }

    // 过滤器
    @Override
    protected Filter[] getServletFilters() {
        // 过滤器1
        CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
        encodingFilter.setEncoding("UTF-8");
        encodingFilter.setForceRequestEncoding(true);
        // 过滤器2
        HiddenHttpMethodFilter hiddenHttpMethodFilter = new
                HiddenHttpMethodFilter();
        return new Filter[]{encodingFilter, hiddenHttpMethodFilter};
    }
}

2 SpringConfig 类:代替 Spring 配置文件(略)

@Configuration
public class MySpringConfig {
    // ...
}

3 WebConfig 类:代替 SpringMVC 配置文件

  • @Bean 的作用是,将方法返回值(Java 对象)交给 IOC 容器
/*
* 需要完成的功能:
* 1.组件扫描
* 2.thymeleaf视图解析器
* 3.视图控制器
* 4.mvc注解驱动
* 5.拦截器
* 6.异常处理器
* 7.default-servlet-handler
* 8.文件上传解析器*/

@Configuration  // 标注当前类为配置类
@ComponentScan(value = {"config", "controller", "interceptor"})  // 开启组件扫描
@EnableWebMvc  // 开启注解驱动
public class MyWebConfig implements WebMvcConfigurer {

    /*************************** 重写WebMvcConfigurer类的方法 ***************************/
    // 使用默认的servlet处理静态资源
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    // 拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        MyInterceptor myInterceptor = new MyInterceptor();
        registry.addInterceptor(myInterceptor).addPathPatterns("/**");
    }

    // 视图控制器
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
    }

    // 异常处理器,一种实现方式,也可以配置为 @bean
    @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
        // 写法参考之前的 SpringMVC 配置文件
        Properties properties = new Properties();
        properties.setProperty("java.lang.ArithmeticException", "fail"); // (异常全类名,跳转视图名称)

        SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver();
        simpleMappingExceptionResolver.setExceptionMappings(properties);  // 设置异常映射
        simpleMappingExceptionResolver.setExceptionAttribute("info_key");  // 设置异常信息的key

        resolvers.add(simpleMappingExceptionResolver);
    }

    /************************* 配置 thymeleaf 视图解析器 *******************************/
    // 1.配置生成模板解析器
    @Bean  // @Bean的返回值放入IOC容器
    public ITemplateResolver templateResolver() {
        WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext();
        // ServletContextTemplateResolver需要一个ServletContext作为构造参数,可通过WebApplicationContext 的方法获得
        ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(
                webApplicationContext.getServletContext());
        templateResolver.setPrefix("/templates/");
        templateResolver.setSuffix(".html");
        templateResolver.setCharacterEncoding("UTF-8");
        templateResolver.setTemplateMode(TemplateMode.HTML);
        return templateResolver;
    }

    // 2.生成模板引擎并为其注入模板解析器
    @Bean
    public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) {  // !!!参数进行了自动装配
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(templateResolver);
        return templateEngine;
    }

    // 3.生成视图解析器并为其注入模板引擎
    @Bean
    public ViewResolver viewResolver(SpringTemplateEngine templateEngine) {
        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
        viewResolver.setCharacterEncoding("UTF-8");
        viewResolver.setTemplateEngine(templateEngine);
        return viewResolver;
    }

    /************************* 其它插件 *******************************/
    // 配置文件上传解析器
//    @Bean
//    public CommonsMultipartResolver multipartResolver(){
//        return new CommonsMultipartResolver();
//    }

}

十一 SpringMVC 执行流程

1 常用组件

  • DispatcherServlet:前端控制器,不需要工程师开发,由框架提供
    作用:统一处理请求和响应,整个流程控制的中心,由它调用其它组件处理用户的请求

  • HandlerMapping:处理器映射器,不需要工程师开发,由框架提供
    作用:根据请求的url、method等信息查找Handler,即控制器方法

  • Handler:处理器
    作用:在DispatcherServlet的控制下Handler对具体的用户请求进行处理

  • HandlerAdapter:处理器适配器,不需要工程师开发,由框架提供
    作用:通过HandlerAdapter对处理器(控制器方法)进行执行

  • ViewResolver:视图解析器,不需要工程师开发,由框架提供
    作用:进行视图解析,得到相应的视图,例如:ThymeleafView、InternalResourceView、RedirectView

  • View:视图
    作用:将模型数据通过页面展示给用户

2 流程总结

  1. 用户向服务器发送请求,被 SpringMVC 前端控制器 DispatcherServlet 捕获
  2. DispatcherServlet 解析 URL(统一资源定位器),得到URI(请求资源标识符),并与配置的 servlet-mapping 进行匹配,判断请求 URI 对应的映射:
    (1) 可以匹配,则交给具体的 Servlet,前往步骤3
    (2) 不能匹配,如果配置了 mvc:default-servlet-handler ,访问目标资源(一般为静态资源,如:JS, CSS, HTML),找不到展示404错误
    (3) 不能匹配,没有配置 mvc:default-servlet-handler,展示404错误
  3. 根据 URI,调用 HandlerMapping 获得该 Handler 配置的所有相关的对象(包括 Handler 对象以及Handler 对象对应的拦截器),最后以 HandlerExecutionChain 执行链对象的形式返回
  4. DispatcherServlet 根据获得的 Handler,选择一个合适的 HandlerAdapterHandler 执行需要依赖HandlerAdapter
  5. 如果成功获得 HandlerAdapter,此时将开始执行拦截器的 preHandler() 方法
  6. 向控制器方法传递客户端请求 Request 携带的参数,执行控制器方法,此处可以配置一些额外操作:
    (1) HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
    (2) 数据格式化、数据验证…
  7. 控制器方法执行完成后,向 DispatcherServlet 返回一个 ModelAndView 对象
  8. 执行拦截器的 postHandle() 方法
  9. DispatcherServlet 根据返回的 ModelAndView首先判断是否存在异常:如果存在异常,则执行HandlerExceptionResolver 进行异常处理)选择一个适合的 ViewResolver 进行视图解析,根据 Model 和 View,渲染视图
  10. 执行拦截器的 afterCompletion() 方法
  11. 将渲染结果返回给客户端

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值