您当前的位置:首页 > 电脑百科 > 程序开发 > 框架

SpringMVC:进阶

时间:2020-09-14 11:02:15  来源:  作者:

Ajax 异步交互

SpringMVC 默认用 MAppingJackson2HttpMessageConverter 对 JSON 数据进行转换,需要加入 Jackson 的包;同时在 spring-mvc.xml 使用 <mvc:annotation-driven />

...
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.8</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.9.8</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.9.0</version>
</dependency>
......<!-- 处理器映射器-处理器适配器。进行了功能的增强:支持 json 的读写 -->
<mvc:annotation-driven />
...

@RequestBody

该注解用于 Controller 的方法的形参声明,当使用 Ajax 提交并指定 contentType 为 JSON 形式时,通过 HttpMessageConverter 接口转换为对应的 POJO 对象。

srcmainwebappajax.jsp

<%@ page contentType="text/html;charset=UTF-8" language="JAVA" %>
<html>
<head>
    <title>Ajax</title>
</head>
<body>
<%-- ajax 异步交互 --%>
<button id="btn1">ajax 异步提交</button>
<script src="${pageContext.request.contextPath}/js/jquery-3.5.1.js"></script>
<script>
    $("#btn1").click(function () {
        let url = '${pageContext.request.contextPath}/user/ajaxRequest';
        let data = '[{"id":1,"username":"张三"},{"id":2,"username":"李四"}]';
​
        $.ajax({
            type: 'POST',
            url: url,
            data: data,
            contentType: 'application/json;charset=utf-8',
            success: function (resp) {
                alert(JSON.stringify(resp));
            }
        })
    });
</script>
</body>
</html>

在 UserController 中添加方法

@RequestMapping("/ajaxRequest")
public List<User> ajaxRequest(@RequestBody List<User> list){
    System.out.println(list);
}

@ResponseBody

该注解用于将 Controller 的方法返回的对象,通过 HttpMessageConverter 接口转换为指定格式的数据如:JSON,xml 等,通过 Response 响应给客户端。

@RequestMapping("/ajaxRequest")
@ResponseBody
public List<User> ajaxRequest(@RequestBody List<User> list){
    System.out.println(list);
    return list;
}

 

RESTful

什么是 RESTful

Restful 是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。主要用于客户端和服务器交互类的软件,基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存机制等。

Restful 风格的请求是使用“URL + 请求方式”表示一次请求目的的,HTTP 协议里面四个表示操作方式的动词如下:

  • GET:读取(Read)
  • POST:新建(Create)
  • PUT:更新(Update)
  • DELETE:删除(Delete)

查询所有:/user/findAll GET /user

根据 ID 查询:/user/findById?id=3 GET /user/{1}

新增:/user/save POST /user

修改:/user/update PUT /user

删除:/user/delete?id=3 DELETE /user/{1}

代码实现

@PathVariable

用来接收 RESTful 风格请求地址中占位符的值。

@RestController

RESTful 风格多用于前后端分离项目开发,前端通过 Ajax 与服务器进行异步交互,我们处理器通常返回的是 JSON 数据所以使用 @RestController 来替代 @Controller 和 @ResponseBody 两个注解。

/**
 * 没有 ResponseBody 的话,会把 return 的值作为逻辑视图进行解析;
 * 带有 ResponseBody 则直接进行数据的响应 */
@RestController // @RestController = @Controller + @ResponseBody
@RequestMapping("/restful")
public class RestfulController {
​
    /**
     * 根据 id 进行查询
     */
    @GetMapping("/user/{id}") // @RequestMapping(value = "/user/{id}",method = RequestMethod.GET)
    public String findById(@PathVariable Integer id) {
        // 获取 restful 编程风格中 url 里面占位符的值
        return "findById: " + id;
    }
​
    /**
     * 新增方法
     * POST 对应的是新增
     */
    @PostMapping("/user") // @RequestMapping(value = "/user",method = RequestMethod.POST)
    public String post(){
        return "post";
    }
​
    /**
     * 更新方法
     * PUT 对应的是更新操作
     */
    @PutMapping("/user")
    public String put(){
        return "put";
    }
​
    /**
     * 删除方法
     */
    @DeleteMapping("/user/{id}")
    public String delete(@PathVariable Integer id){
        return "delete" + id;
    }
​
}

 

文件上传

文件上传三要素

  • 表单项 type="file"
  • 表单的提交方式 method="POST"
  • 表单的 enctype 属性是多部分表单形式 enctype=“multipart/form-data"
<form action="${pageContext.request.contextPath}/fileupload" method="post" enctype="multipart/form-data">
    名称:<input type="text" name="username"> <br>
    文件:<input type="file" name="filePic"> <br>
    <input type="submit" value="单文件上传">
</form>

文件上传原理

当 form 表单的 enctype 取值为 application/x-www-form-urlencoded 时,form 表单的正文内容格式是: name=value&name=value。

当 form 表单的 enctype 取值为 mutilpart/form-data 时,请求正文内容就变成多部分形式:

当 form 表单修改为多部分表单时,request.getParameter() 将失效。

输入表单项名称 username 为 "张人大",上传文件 filePic 为 "a.txt",其中文件的内容为 "test renda";此时表单的 Request Body 的有效载荷 payload 如下:

-----------------------------17656195882531319514853385408
Content-Disposition: form-data; name="username"
​张人大-----------------------------17656195882531319514853385408
Content-Disposition: form-data; name="filePic"; filename="a.txt"
Content-Type: text/plain​test renda-----------------------------17656195882531319514853385408--

单文件上传

步骤分析:

  1. 导入 fileupload 和 io 坐标
  2. 配置文件上传解析器
  3. 编写文件上传代码

导入 fileupload 和 io 坐标

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.3</version>
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>

配置文件上传解析器

spring-mvc.xml

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- 设定文件上传的最大值为 5 MB = 5*1024*1024 B -->
    <property name="maxUploadSize" value="5242880"/>
    <!-- 设定文件上传时写入内存的最大值,如果小于这个参数不会生成临时文件,默认为 10240 -->
    <property name="maxInMemorySize" value="40960"/>
</bean>

编写文件上传代码

<form action="${pageContext.request.contextPath}/fileupload" method="post" enctype="multipart/form-data">
    名称:<input type="text" name="username"> <br>
    文件:<input type="file" name="filePic"> <br>
    <input type="submit" value="单文件上传">
</form>@RequestMapping("/fileupload")
public String fileUpload(String username, MultipartFile filePic) throws IOException {    // 获取表单的提交参数,完成文件上传    System.out.println(username);    // 获取原始的文件上传名    String originalFilename = filePic.getOriginalFilename();    filePic.transferTo(new File("E:/upload/" + username + "_" + originalFilename));
    // 转发到成功页面    return "success";
}

多文件上传

<form action="${pageContext.request.contextPath}/filesupload" method="post" enctype="multipart/form-data">
    名称:<input type="text" name="username"> <br>
    文件1:<input type="file" name="filePic"> <br>
    文件2:<input type="file" name="filePic"> <br>
    <input type="submit" value="多文件上传">
</form>@RequestMapping("/filesupload")
public String filesUpload(String username, MultipartFile[] filePic) throws IOException {    //获取表单的提交参数,完成文件上传    System.out.println(username);    // 获取原始的文件上传名    for (MultipartFile multipartFile : filePic) {
        String originalFilename = multipartFile.getOriginalFilename();        multipartFile.transferTo(new File("E:/upload/" + username + "_" + originalFilename));
    }    // 转发到成功页面    return "success";
}

 

异常处理

异常处理的思路

在 Java 中,对于异常的处理一般有两种方式:

  • 一种是当前方法捕获处理(try-catch),这种处理方式会造成业务代码和异常处理代码的耦合。
  • 另一种是自己不处理,而是抛给调用者处理(throws),调用者再抛给它的调用者,也就是一直向上抛。在这种方法的基础上,衍生出了 SpringMVC 的异常处理机制。

系统的 Dao、Service、Controller 出现都通过 throws Exception 向上抛出,最后由 SpringMVC 前端控制器交由异常处理器(HandlerExceptionResolver)进行异常处理:

请求往下传:客户端 -> 前端控制器 -> Controller -> Service -> Dao
异常往上抛:Dao -> Service -> Controller -> 前端控制器 -> 异常处理器

自定义异常处理器

步骤分析:

  1. 创建异常处理器类实现 HandlerExceptionResolver
  2. 配置异常处理器
  3. 编写异常页面
  4. 测试异常跳转

创建异常处理器类实现 HandlerExceptionResolver

com.renda.exception.GlobalExceptionResolver

public class GlobalExceptionResolver implements HandlerExceptionResolver {
​    /**
     * @param e 实际抛出的异常对象
     */
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        // 具体的异常处理:产生异常后,跳转到一个最终的异常页面
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("error", e.getMessage());
        modelAndView.setViewName("error");
        return modelAndView;
    }
​
}

配置异常处理器

spring-mvc.xml

<bean id="globalExceptionResolver" class="com.renda.exception.GlobalExceptionResolver"/>

编写异常页面

srcmainwebappWEB-INFpageserror.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>ERROR</title>
</head>
<body>
​ERROR:${error}​</body>
</html>

测试异常跳转

com.renda.controller.ExceptionController

@Controller
public class ExceptionController {
    @RequestMapping("/testException")
    public String testException(){
        int i = 1/0;
        return "success";
    }}

web 的处理异常机制

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>404 Error</title>
</head>
<body>
    Renda: 您请求的资源已经删除</body>
</html>
​<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>500 Error</title>
</head>
<body>
    Renda: 网络故障,请稍后再试</body>
</html><!-- 处理 404 异常 -->
<error-page>
    <error-code>404</error-code>
    <location>/404.jsp</location>
</error-page>
<!-- 处理 500 异常 -->
<error-page>
    <error-code>500</error-code>
    <location>/500.jsp</location>
</error-page>

 

拦截器

拦截器(interceptor)的作用

Spring MVC 的拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理。

将拦截器按一定的顺序联结成一条链,这条链称为拦截器链 InterceptorChain。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。拦截器也是 AOP 思想的具体实现。

拦截器和过滤器区别

使用范围:

filter - 是 servlet 规范中的一部分,任何 Java Web 工程都可以使用 。

interceptor - 是 SpringMVC 框架的一部分,只有使用了 SpringMVC 框架的工程才能用。

拦截范围:

filter - 在 url-pattern 中配置了 /* 后,可以对所有资源进行过滤拦截。

interceptor - 只会拦截访问控制器方法,如果访问的是 JSP、HTML、css、Image、JS 就不会进行拦截。

快速入门

步骤分析:

  1. 创建拦截器类实现 HandlerInterceptor 接口
  2. 配置拦截器
  3. 测试拦截器的拦截效果

创建拦截器类实现 HandlerInterceptor 接口

com.renda.interceptor.MyInterceptor1

public class MyInterceptor1 implements HandlerInterceptor {
    /**
     * 在目标方法执行之前进行拦截
     *
     * @return false:不放行
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle1....");
        return true;
    }​    /**
     * 在目标方法执行之后,视图对象返回之前,执行的方法
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle1....");
    }​    /**
     * 在流程都执行完成后,执行的方法
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion1....");
    }}

配置拦截器

spring-mvc.jsp

<mvc:interceptors>
    <mvc:interceptor>
        <!-- 对所有 controller 类里面的所有方法都进行拦截 -->
        <mvc:mapping path="/**"/>
        <bean class="com.renda.interceptor.MyInterceptor1"></bean>
    </mvc:interceptor>
</mvc:interceptors>

测试拦截器的拦截效果

编写 com.renda.controller.TargetController,请求到 controller,跳转页面

@Controller
public class TargetController {
    @RequestMapping("/target")
    public String targetMethod() {
        System.out.println("targetMethod executed ...");
        return "success";
    }}

success.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>SUCCESS</title>
</head>
<body>
​    <h3>success... ${username}</h3>
    <% System.out.println("Success...");%>
​</body>
</html>

控制台输出结果:

preHandle1....
targetMethod executed ...
postHandle1....
Success...
afterCompletion1....

拦截器链

开发中拦截器可以单独使用,也可以同时使用多个拦截器形成一条拦截器链。开发步骤和单个拦截器是一样的,只不过注册的时候注册多个,注意这里注册的顺序就代表拦截器执行的顺序。

同上,再编写一个 MyHandlerInterceptor2 操作:

public class MyInterceptor2 implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle2....");
        return true;
    }​    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle2....");
    }​    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion2...");
    }}

spring-mvc.jsp

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="com.renda.interceptor.MyInterceptor1"></bean>
    </mvc:interceptor>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="com.renda.interceptor.MyInterceptor2"></bean>
    </mvc:interceptor>
</mvc:interceptors>

控制台输出结果:

preHandle1....
preHandle2....
targetMethod executed ...
postHandle2....
postHandle1....
Success...
afterCompletion2...
afterCompletion1....

当MyInterceptor1 的 preHandler 方法返回 true,MyHandlerInterceptor2 的 preHandler 方法返回 false;此时,MyInterceptor1 的 preHandler 方法优先执行并返回 true,即便MyHandlerInterceptor2 的 preHandler 方法返回 false,MyInterceptor1 的 afterCompletion 方法仍然被执行了。控制台输出结果:

preHandle1....
preHandle2....
afterCompletion1....

当 MyInterceptor1 的 preHandler 方法返回 false,MyHandlerInterceptor2 的 preHandler 方法返回 true;此时,MyInterceptor1 的 preHandler 方法优先执行并返回 false,方法被拦截,MyHandlerInterceptor2 的 preHandler 方法也无法执行。控制台输出结果:

preHandle1....

所以形成拦截器链时,当拦截器 1 的 preHandler 方法成功执行并返回 true 后,被它拦截的方法即便被另一个拦截器 2 所拦截并返回了 false,拦截器 1 的afterCompletion 方法仍然会被执行。

源码解释:

/**
 * Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
 * Will just invoke afterCompletion for all interceptors whose preHandle invocation
 * has successfully completed and returned true.
 */
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
    throws Exception {
    ...}

 

小结

拦截器中的方法说明:

  • preHandle() - 方法将在请求处理之前进行调用,该方法的返回值是布尔值类型的,当它返回为 false 时,表示请求结束,后续的 Interceptor 和 Controller 都不会再执行;当返回值为 true 时就会继续调用下一个 Interceptor 的 preHandler 方法
  • postHandle() - 该方法是在当前请求进行处理之后被调用,前提是 preHandler 方法的返回值为 true 时才能被调用,且它会在 DispatcherServlet 进行视图返回渲染之前被调用,所以可以在这个方法中对 Controller 处理之后的 ModelAndView 对象进行操作
  • afterCompletion() - 该方法在整个请求结束之后,就是在DispatcherServlet 渲染了对应的视图之后执行,前提是 preHandler 方法的返回值为 true 时才能被调用

想了解更多,欢迎关注我的微信公众号:Renda_Zhang



Tags:SpringMVC   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
本篇文章我们来学习一下SpringMVC中是如何处理请求中的参数的。回想一下原生Servlet是如何处理请求参数的?我们需要使用HttpServletRequest调用getParameter方法进行获取,就像...【详细内容】
2021-08-16  Tags: SpringMVC  点击:(70)  评论:(0)  加入收藏
背景:项目是老项目,而且比较旧为springmvc项目。项目使用的框架为公司内部自己开发,目前已经没有框架的源码可供修改,配置文件写在底层框架内,可以看到但修改不到。目的是为了实...【详细内容】
2021-08-11  Tags: SpringMVC  点击:(70)  评论:(0)  加入收藏
一、什么是springmvc?我们知道三层架构的思想,并且如果你知道ssh的话,就会更加透彻地理解这个思想,struts2在web层,spring在中间控制,hibernate在dao层与数据库打交道,而前面刚写...【详细内容】
2021-07-14  Tags: SpringMVC  点击:(93)  评论:(0)  加入收藏
前言学了一遍SpringMVC以后,想着做一个总结,复习一下。复习写下面的总结的时候才发现,其实自己学得并不彻底、牢固、也没有学全,视频跟书本是要结合起来一起,每一位老师的视频可...【详细内容】
2021-03-08  Tags: SpringMVC  点击:(193)  评论:(0)  加入收藏
对Java程序员来讲,做web开发最熟悉的框架莫过于SpringMVC了。之所以它能一统江湖,不是自己太优秀,而是对手太坑了,不知道大家还记不记得2017年左右Struts2爆出了一个大漏洞,自此...【详细内容】
2020-10-14  Tags: SpringMVC  点击:(66)  评论:(0)  加入收藏
Ajax 异步交互SpringMVC 默认用 MappingJackson2HttpMessageConverter 对 JSON 数据进行转换,需要加入 Jackson 的包;同时在 spring-mvc.xml 使用 <mvc:annotation-driven />....【详细内容】
2020-09-14  Tags: SpringMVC  点击:(75)  评论:(0)  加入收藏
MVC是英文Modle View Controller的简称,是一种软件设计典范,目的是将业务逻辑、数据、页面视图代码分离,达到增加开发效率、降低耦合度、代码更利于维护的目的。 Spring MVC是...【详细内容】
2020-08-06  Tags: SpringMVC  点击:(53)  评论:(0)  加入收藏
前言大家好,我是bigsai,今天我们学习SpringMVC的文件上传下载。文件上传和下载是互联网web应用非常重要的组成部分,它是信息交互传输的重要渠道之一。你可能经常在网页上传下...【详细内容】
2020-07-30  Tags: SpringMVC  点击:(57)  评论:(0)  加入收藏
今天就由本大佬(请原谅我使用了略微夸张的修辞手法)亲自带队,来为大家导游,带领大家探秘神奇的SpringMVC世界,重走一次HTTP请求处理之路,本次行程共计7站,约用时10分钟。 来来来,上...【详细内容】
2020-03-24  Tags: SpringMVC  点击:(110)  评论:(0)  加入收藏
这节介绍SpringMVC,SpringMVC是一种基于Java的实现MVC设计模式的请求驱动类型的轻量级Web框架。本章会介绍相关概念,流程,再从源码进行讲解。1. MVC&emsp;MVC(Model View Contr...【详细内容】
2020-01-07  Tags: SpringMVC  点击:(50)  评论:(0)  加入收藏
▌简易百科推荐
近日只是为了想尽办法为 Flask 实现 Swagger UI 文档功能,基本上要让 Flask 配合 Flasgger, 所以写了篇 Flask 应用集成 Swagger UI 。然而不断的 Google 过程中偶然间发现了...【详细内容】
2021-12-23  Python阿杰    Tags:FastAPI   点击:(6)  评论:(0)  加入收藏
文章目录1、Quartz1.1 引入依赖<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.2</version></dependency>...【详细内容】
2021-12-22  java老人头    Tags:框架   点击:(11)  评论:(0)  加入收藏
今天来梳理下 Spring 的整体脉络啦,为后面的文章做个铺垫~后面几篇文章应该会讲讲这些内容啦 Spring AOP 插件 (了好久都忘了 ) 分享下 4ye 在项目中利用 AOP + MybatisPlus 对...【详细内容】
2021-12-07  Java4ye    Tags:Spring   点击:(14)  评论:(0)  加入收藏
&emsp;前面通过入门案例介绍,我们发现在SpringSecurity中如果我们没有使用自定义的登录界面,那么SpringSecurity会给我们提供一个系统登录界面。但真实项目中我们一般都会使用...【详细内容】
2021-12-06  波哥带你学Java    Tags:SpringSecurity   点击:(18)  评论:(0)  加入收藏
React 简介 React 基本使用<div id="test"></div><script type="text/javascript" src="../js/react.development.js"></script><script type="text/javascript" src="../js...【详细内容】
2021-11-30  清闲的帆船先生    Tags:框架   点击:(19)  评论:(0)  加入收藏
流水线(Pipeline)是把一个重复的过程分解为若干个子过程,使每个子过程与其他子过程并行进行的技术。本文主要介绍了诞生于云原生时代的流水线框架 Argo。 什么是流水线?在计算机...【详细内容】
2021-11-30  叼着猫的鱼    Tags:框架   点击:(21)  评论:(0)  加入收藏
TKinterThinter 是标准的python包,你可以在linx,macos,windows上使用它,你不需要安装它,因为它是python自带的扩展包。 它采用TCL的控制接口,你可以非常方便地写出图形界面,如...【详细内容】
2021-11-30    梦回故里归来  Tags:框架   点击:(26)  评论:(0)  加入收藏
前言项目中的配置文件会有密码的存在,例如数据库的密码、邮箱的密码、FTP的密码等。配置的密码以明文的方式暴露,并不是一种安全的方式,特别是大型项目的生产环境中,因为配置文...【详细内容】
2021-11-17  充满元气的java爱好者  博客园  Tags:SpringBoot   点击:(25)  评论:(0)  加入收藏
一、搭建环境1、创建数据库表和表结构create table account(id INT identity(1,1) primary key,name varchar(20),[money] DECIMAL2、创建maven的工程SSM,在pom.xml文件引入...【详细内容】
2021-11-11  AT小白在线中  搜狐号  Tags:开发框架   点击:(29)  评论:(0)  加入收藏
SpringBoot开发的物联网通信平台系统项目功能模块 功能 说明 MQTT 1.SSL支持 2.集群化部署时暂不支持retain&will类型消 UDP ...【详细内容】
2021-11-05  小程序建站    Tags:SpringBoot   点击:(55)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条