1、SpringMVC 总结

1、配置

1.1、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">

    <!--Spring 中提供的字符编码过滤器-->
    <filter>
        <filter-name>encFilter</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>
    </filter>
    <filter-mapping>
        <filter-name>encFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>


    <!--配置 DispatcherServlet -->
    <!--配置前端控制器-->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--初始化参数: 指定 SpringMVC 配置文件的路径-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        <!--启动立即初始化 Servlet-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <!--配置 dispatcherServlet 的映射路径为 / 包含全部的 servlet, JSP 除外-->
        <url-pattern>/</url-pattern>
    </servlet-mapping>


    <!--配置 hiddenHttpMethodFilter, 将 POST 请求转换为 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>

</web-app>

1.2、spring-mvc.xml

// 自定义类型转换器
// 内置的日期类型转换器:2020/05/01(不⽀持 2020-05-01)
public class DateConverter implements Converter<String, Date> {

    @Override
    public Date convert(String s) {
        Date date = null;
        try {
            date = new SimpleDateFormat("yyyy-MM-dd").parse(s);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }
}
<?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:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util
       http://www.springframework.org/schema/util/spring-util.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd">


    <!--配置 spring 包扫描-->
    <context:component-scan base-package="com.zzw.controller" conversion-service="converter"/>
    <!--类型转换器-->
    <bean id="converter" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <bean class="com.zzw.converter.DateConverter"/>
            </set>
        </property>
    </bean>

    <!--处理器映射器-->
    <!--<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>-->
    <!--处理器适配器-->
    <!--<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>-->

    <!--开启 MVC 注解驱动, 自动配置: 处理器映射器和处理器适配器-->
    <!--使用 <mvc:annotation-driven> 默认底层就会集成 jackson 进行对象或集合的 json 格式字符串的转换-->
    <mvc:annotation-driven />

    <!--视图解析器-->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--指定路径的前缀和后缀-->
        <property name="prefix" value="/WEB-INF/view/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!--静态资源释放方式一-->
    <!--mapping:url, location:资源位置目录-->
    <mvc:resources mapping="/static/**" location="/static/"/>
    <!--
        静态资源释放方式二
        当前端控制器调用处理器映射器去查找资源不存在的时候, 先不要报错
        而是选择将这个请求转交给程序外部容器(tomcat)的默认 servlet 处理(default)
    -->
    <mvc:default-servlet-handler/>
</beans>

1.3、文件上传

导入依赖

<!--文件上传-->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
</dependency>

在 spring-mvc.xml 中配置

<!--文件上传解析器, id 必须是固定的, 不能改, multipartResolver-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!--单次上传文件大小, 单位 B(B -> KB -> MB), 10 MB = 10 * 1024 * 1024 B-->
    <property name="maxUploadSize" value="10485760"/>
</bean>

2、执行流程

前端控制器 DispatcherServlet 三大组件

  • 处理器映射器:Handler Mapping
  • 处理器适配器:Handler Adapter
  • 视图解析器:View Resolver

image

  • 用户发送请求至前端控制器 DispatcherServlet
  • DispatcherServlet 收到请求,调用 HandlerMapping 处理器映射器
  • 处理器映射器找到具体的处理器(可以根据 xml 配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给 DispatcherServlet
  • DispatcherServlet 调用 HandlerAdapter 处理器适配器
  • HandlerAdapter 经过适配调用具体的处理器(Controller,也叫后端控制器)
  • Controller 执行完成返回 ModelAndView
  • HandlerAdapter 将 controller 执行结果 ModelAndView 返回给 DispatcherServlet
  • DispatcherServlet 将 ModelAndView 传给 ViewReslover 视图解析器
  • ViewReslover 解析后返回具体 View
  • DispatcherServlet 根据 View 进行渲染视图(即将模型数据填充至视图中),DispatcherServlet 响应用户

3、请求控制

3.1、@RequestMapping

  • 控制请求方式(method)
    • @RequestMapping(value = "/***" , method = {RequestMethod.GET})
    • value / path = "URL"
    • method:Get、Post、Put、Delete
  • 控制请求参数(param)
    • param: 表示请求中必须包含名为 param 的参数
    • !param:表示请求中不能包含名为 param 的参数
    • param = value:表示请求中包含名为 param 的参数,但是值必须是 value
    • param != value:表示请求中包含名为 param 的参数,但是值不能是 value
    • {"param1", "param2 = value"}:可以将对于多个参数的要求写入数组
  • 请求头(headers)
@Controller
@RequestMapping("/zzw")
public class MyController {

    // 定义一个处理请求的方法
    // 返回值是 String, 用来表明要跳转页面的路径
    // 这个字符串会到 spring-mvc.xml 中配置的视图解析器进行解析
    @RequestMapping(path = {"/firstController.do"})
    public String firstController() {
        System.out.println("firstController");
        return "first";
    }

    // 对请求方式进行控制
    @RequestMapping(path = "/testRequest1.do", method = {RequestMethod.POST})
    public String testRequest1() {
        System.out.println("testRequest1");
        return "first";
    }

    // 对请求参数进行控制
    @RequestMapping(path = "/testRequest2.do", params = {"username != root", "password"})
    public String testRequest2() {
        System.out.println("testRequest2");
        return "first";
    }

    // 对请求头进行控制
    @RequestMapping(path = "/testRequest3.do", headers = {"Accept-Encoding = gzip, deflate"})
    public String testRequest3() {
        System.out.println("testRequest3");
        return "first";
    }
}

3.2、@PathVariable

@PathVariable 注解和 restful 风格的支持

@Controller
public class PathController {

    @RequestMapping(path = "/testPathVariable/{id}/{username}")
    public String testPathVariable(@PathVariable("id") String id, @PathVariable("username") String username) {
        System.out.println("id:" + id);
        System.out.println("username:" + username);
        System.out.println("testPathVariable1");
        return "first";
    }
}

4、获取请求参数

4.1、普通类型 @RequestParam

@RequestParam:把请求中指定名称的参数给控制器中的形参赋值,名称参数不一致时可以使用它

  • value:请求参数中的名称
  • required:请求参数中是否必须提供此参数,默认为 true,表示必须提供,如果不提供将报错
  • defaultValue:当没有指定请求参数时,则使用指定的默认值赋值
// HttpServletRequest 对象获取参数, 通过 SpringMVC 框架功能, 自动转换参数
// 处理单元参数列表中参数名必须和请求中的参数名一致
// 如不一致, 可以通过 @RequestParma 注解进行转换
@RequestMapping("/test")
public String test(String username, @RequestParam("pwd") String password) {
    System.out.println("username:" + username);
    System.out.println("password:" + password);
    return "getParamSuccess";
}

http://localhost:8080/zzw/test?usErnaMe=&passwoRd=123&pagesize=5&students=张三&students=李四

// @RequestParam 注解一般标在请求参数的前面
// 作用 1: 用于指定当前参数是接收的前端传入的哪个参数的值
// 作用 2: 可以为请求参数设置默认值
// 作用 3: 当 @RequestParam 标注在一个参数之前时, 这个参数默认是必选要传值的, 但是可以使用 required = false 取消限制
// 作用 4: 可以使用他来标注接收一个 List 参数
@RequestMapping("/test")
public String test(@RequestParam("usErnaMe") String username, @RequestParam("passwoRd") String password,
                   @RequestParam(defaultValue = "1") Integer pageNum, @RequestParam(defaultValue = "10") Integer pageSize,
                   @RequestParam(value = "sex", required = false) String sex,
                   @RequestParam("students") List<String> students) {
    System.out.println(username + ":" + password);
    System.out.println(pageNum + ":" + pageSize);
    System.out.println(sex);
    System.out.println(Arrays.toString(students.toArray()));
    return "success";
}

4.2、数组类型

http://localhost:8080/zzw/test?strs=111&strs=222&strs=333

@RequestMapping("/test")
public void test(String[] strs) {
    System.out.println(Arrays.asList(strs));
}

4.3、对象类型

  • 提交的参数名必须和 POJO 的属性名保持一致
  • springmvc 底层通过反射给参数列表的属性赋值
    通过 set 方法设置属性值的,不是直接通过操作属性
    POJO 的属性一定要有 set 方法,要不然就会接收失败
@RequestMapping("/test")
public void test(User user) {
    System.out.println(user);
}

4.4、集合类型 @RequestBody

  • 要么 POJO 封装集合来完成
  • 要么 AJAX 异步完成:当使用 ajax 提交时,可以指定 contentType 为 json 形式
    那么在方法参数位置使用 @RequestBody 可以直接收集合数据而无需使用 POJO 进行包装

@RequestBody

  • required:是否必须有请求体,默认为 true
    当取值为 true 时,get 请求方式会报错
    如果取值为 false,get 请求得到是 null
  • 标注在参数前面,用于将请求体(get 没有请求体)中的 Json 数据转换成指定的对象
    如果没有具体的实体类来接收参数,就要用 Map 来接收
@RequestMapping("/test")
public void test(@RequestBody List<User> userList) throws IOException {
    System.out.println(userList);
}
<script>
    //模拟数据
    var userList = new Array();
    userList.push({username: "zhangsan",age: "20"});
    userList.push({username: "lisi",age: "20"});

    $.ajax({
        type: "POST", // 请求方式必须是 POST, GET 不适用
        url: "/zzw/test",
        data: JSON.stringify(userList),
        contentType : 'application/json;charset=utf-8'
    });
</script>

4.5、@RequestHeader

用于获取请求消息头

  • value:提供消息头名称
  • required:是否必须有此消息头

@RequestHeader 注解可以获取请求头中的数据,并封装到指定的 map 中
@RequestHeader(headerKey) 指定的键接收请求头中某一项的值,例如 @RequestHeader("cookie") 就是接收请求头中的 cookie 的值

/*
 * 接收请求头
 * @RequestHeader 将请求头封装到指定的一个 map 结构中
 * @RequestHeader(headerKey) 直接根据请求头中得键来获取值
 */
@RequestMapping("/test")
public String test(@RequestHeader Map<String, String> headers, @RequestHeader("cookie") String cookie) {
    System.out.println(headers);
    System.out.println(headers.get("cookie"));

    System.out.println(cookie);

    return "success";
}

4.6、@CookieValue

用于把指定 cookie 名称的值传入控制器方法参数

  • value:提供消息头名称
  • required:是否必须有此 cookie

4.7、@DateTimeFormat

日期类型转换 @DateTimeFormat(pattern = "yyyy-MM-dd") 可以用于:方法参数列表、类的属性

@RequestMapping("/test")
public string test(@DateTimeFormat(pattern "yyyy-MM-dd") Date birthday){
    System.out.println(birthday);
    return "success";
}
@Data
@ALLArgsconstructor
@NoArgsconstructor
public class Person implements Serializable {

    private String name;
    private String age;
    private string gender;
    private String[]hobby;

    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birthdate;
}

5、响应

<!--视图解析器-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/" />
    <property name="suffix" value=".jsp" />
</bean>
* 请求转发
    - return "success";                                                                 会去拼接配置文件中的前后缀
    - return "forward:/error.jsp";                                                      写全路径
    - request.getRequestDispatcher("/WEB-INF/success.jsp").forward(request, response);  写全路径

* 转发过程中传递数据:存入 request 域
    - request.setAttribute("username", "zw");
    - model.addAttribute("username", "zw");
    - ModelAndView mv = new ModelAndView();
      mv.setViewName("success");                 会去拼接配置文件中的前后缀
      mv.addObject("username", "zw");
      return mv;

* 响应重定向
    - return "redirect:/error.jsp";                                    写全路径
    - response.sendRedirect(request.getContextPath() + "/error.jsp");  写全路径

5.1、@ResponseBody

  • 方法的返回值不再作为界面跳转依据,将方法返回的数据自动转换为 JSON(使用 ObjectMapper)
  • 把我们要响应的数据直接 return 即可,返回值类型为要 return 的数据类型
  • 也可以这样回写数据:response.getWriter().print("hello world");

5.2、@RestController

@RestController = @Controller + @ResponseBody

  • 相当于 @Controller + @ResponseBody 两个注解的结合,返回 json 数据,不需要在方法前面加 @ResponseBody 注解了
  • 但使用 @RestController 这个注解,就不能返回 jsp、html 页面,视图解析器无法解析 jsp、html 页面

5.3、@JsonFormat

@JsonFormat 可以用于:方法参数列表、类的属性,响应 json 数据的处理

  • pattern:指定响应时间日期的格式
  • Timezone:指定响应的时区,否则会有 8 个小时的时间差
@DateTimeFormat(pattern = "yyyy-MM-dd")                   // 入参
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")   // 出参
private Date birth;

6、异常处理

* 对于异常的处理一般有两种方式
    - 当前方法处理(try-catch),这种处理方式会造成业务代码和异常处理代码的耦合
    - 当前方法不处理,出现异常后直接抛给调用者处理
* 使用 Spring 框架后,我们的代码最终是由框架来调用的
* 也就是说,异常最终会抛到框架中,然后由框架指定异常处理器来统一处理异常

image

6.1、配置方式

* SpringMVC 允许自定义一个全局异常处理类来统一处理异常
* 它要求这个类必须实现 HandlerExceptionResolver 接口,并重写类中异常的处理方法
@Component
public class GlobalExceptionHandler implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest,
                                         HttpServletResponse httpServletResponse, Object o, Exception e) {

        if (e instanceof ArithmeticException) {
            System.out.println("数学异常");
        } else if (e instanceof NullPointerException) {
            System.out.println("空指针异常");
        } else {
            System.out.println("其它异常");
        }

        ModelAndView mv = new ModelAndView();
        mv.setViewName("forward/error.jsp");
        return mv;
    }
}
<!--注解扫描-->
<context:component-scan base-package="com.zzw.handler"/>

6.2、注解方式

* SpringMVC 支持下面两个注解来实现全局异常处理
    - @ControllerAdvice 要标注在类上,表示当前类是一个全局异常处理器的类
    - @ExceptionHandler 标注在方法上,表示当前方法可以处理哪些异常
@ControllerAdvice // 表示当前这是一个全局异常处理器, 返回 Json 数据用 @RestControllerAdvice
public class GlobalExceptionHandlerAnno {

    // 这个方法可以处理 Throwable 异常
    @ExceptionHandler(Throwable.class)
    public String handlerThrowable() {
        System.out.println("大异常");
        return "forward:/error.jsp";
    }

    // 这个方法可以处理 ArithmeticException 异常
    @ExceptionHandler(ArithmeticException.class)
    public String handlerArithmeticException() {
        System.out.println("数学异常");
        return "forward:/error.jsp";
    }

    // 这个方法可以处理 NullPointerException 异常
    @ExceptionHandler(NullPointerException.class)
    public String handlerNullPointerException() {
        System.out.println("空指针异常");
        return "forward:/error.jsp";
    }
}

7、拦截器

7.1、什么是拦截器

拦截器是 Spring 提供的一种技术,它的功能似于过滤器 Filter,它会在进入 controller 之前,离开 controller 之后以及页面渲染完毕之后进行拦截
image

7.2、自定义拦截器

1、开发拦截器

要对拦截的资源做什么,自定义一个类实现 HandlerInterceptor 接口

public class MyHandlerInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("请求进入 controller 之前");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, 
                           HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("请求离开 controller 之后");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, 
                                HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("页面渲染完毕之后");
    }
}

2、配置拦截器

在 spring-mvc.xml 中配置你要拦截哪些资源

<!--配置拦截器链-->
<mvc:interceptors>
    <mvc:interceptor>
        <!--拦截路径-->
        <mvc:mapping path="/**"/>
        <!--不拦截路径-->
        <mvc:exclude-mapping path="/zzw/test"/>
        <!--拦截器处理-->
        <bean class="com.zzw.handler.MyHandlerInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

7.3、自定义拦截器链

<!--配置拦截器链-->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/zzw/test"/>
        <bean class="com.zzw.handler.MyHandlerInterceptor1"/>
    </mvc:interceptor>
    <mvc:interceptor>
        <mvc:mapping path="/zzw/test"/>
        <bean class="com.zzw.handler.MyHandlerInterceptor2"/>
    </mvc:interceptor>
</mvc:interceptors>
// 结果
// 请求进入 controller1 之前
// 请求进入 controller2 之前
// 到达了后台 test
// 请求离开 controller2 之后
// 请求离开 controller1 之后
// 页面渲染完毕 2 之后
// 页面渲染完毕 1 之后

image

posted @ 2023-07-13 16:41  lidongdongdong~  阅读(17)  评论(0)    收藏  举报