千家信息网

Java中SpringMVC的使用方法

发表于:2025-02-01 作者:千家信息网编辑
千家信息网最后更新 2025年02月01日,今天就跟大家聊聊有关Java中SpringMVC的使用方法,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。目录设计Restful接口Spring
千家信息网最后更新 2025年02月01日Java中SpringMVC的使用方法

今天就跟大家聊聊有关Java中SpringMVC的使用方法,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。

目录
  • 设计Restful接口

  • SpringMVC

    • 项目整合SpringMVC

  • 使用SpringMVC实现Restful接口

    • 逻辑交互

      • 身份认证

      • 计时面板

    • 总结

      设计Restful接口

      根据需求设计前端交互流程。

      三个职位:

      • 产品:解读用户需求,搞出需求文档

      • 前端:不同平台的页面展示

      • 后端:存储、展示、处理数据

      前端页面流程:

      详情页流程逻辑:

      标准系统时间从服务器获取。

      Restful:一种优雅的URI表述方式、资源的状态和状态转移。

      Restful规范:

      • GET 查询操作

      • POST 添加/修改操作(非幂等)

      • PUT 修改操作(幂等,没有太严格区分)

      • DELETE 删除操作

      URL设计:

      /模块/资源/{标示}/集合/.../user/{uid}/friends -> 好友列表/user/{uid}/followers -> 关注者列表

      秒杀API的URL设计

      GET /seckill/list 秒杀列表GET /seckill/{id}/detail 详情页GET /seckill/time/now 系统时间POST /seckill/{id}/exposer 暴露秒杀POST /seckill/{id}/{md5}/execution 执行秒杀

      下一步就是如何实现这些URL接口。

      SpringMVC

      理论

      适配器模式(Adapter Pattern),把一个类的接口变换成客户端所期待的另一种接口, Adapter模式使原本因接口不匹配(或者不兼容)而无法在一起工作的两个类能够在一起工作。

      SpringMVC的handlerControllerHttpRequestHandlerServlet等)有多种实现方式,例如继承Controller的,基于注解控制器方式的,HttpRequestHandler方式的。由于实现方式不一样,调用方式就不确定了。

      看HandlerAdapter接口有三个方法:

      // 判断该适配器是否支持这个HandlerMethodboolean supports(Object handler);// 用来执行控制器处理函数,获取ModelAndView 。就是根据该适配器调用规则执行handler方法。ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;long getLastModified(HttpServletRequest request, Object handler);

      问流程如上图,用户访问一个请求,首先经过DispatcherServlet转发。利用HandlerMapping得到想要的HandlerExecutionChain(里面包含handler和一堆拦截器)。然后利用handler,得到HandlerAdapter,遍历所有注入的HandlerAdapter,依次使用supports方法寻找适合这个handler的适配器子类。最后通过这个获取的适配器子类运用handle方法调用控制器函数,返回ModelAndView。

      注解映射技巧

      • 支持标准的URL

      • ?和*和**等字符,如/usr/*/creation会匹配/usr/AAA/creation和/usr/BBB/creation等。/usr/**/creation会匹配/usr/creation和/usr/AAA/BBB/creation等URL。带{xxx}占位符的URL。

      • 如/usr/{userid}匹配/usr/123、/usr/abc等URL.

      请求方法细节处理

      • 请求参数绑定

      • 请求方式限制

      • 请求转发和重定向

      • 数据模型赋值

      • 返回json数据

      • cookie访问

      返回json数据

      cookie访问:

      项目整合SpringMVC

      web.xml下配置springmvc需要加载的配置文件:

                 seckill-dispatcher    org.springframework.web.servlet.DispatcherServlet                  contextConfigLocation      classpath:spring/spring-*.xml            seckill-dispatcher    /  

      在resources文件夹下的spring文件夹添加spring-web.xml文件:

                                                                              

      使用SpringMVC实现Restful接口

      新建文件:

      首先是SeckillResult.java,这个保存controller的返回结果,做一个封装。

      // 所有ajax请求返回类型,封装json结果public class SeckillResult {    private boolean success; //是否执行成功    private T data; // 携带数据    private String error; // 错误信息    // getter setter contructor}

      在Seckillcontroller.java中,实现了我们之前定义的几个URL:

      GET /seckill/list 秒杀列表GET /seckill/{id}/detail 详情页GET /seckill/time/now 系统时间POST /seckill/{id}/exposer 暴露秒杀POST /seckill/{id}/{md5}/execution 执行秒杀

      具体代码如下:

      @Controller // @Service @Component放入spring容器@RequestMapping("/seckill") // url:模块/资源/{id}/细分public class SeckillController {    private final Logger logger = LoggerFactory.getLogger(this.getClass());    @Autowired    private SecKillService secKillService;    @RequestMapping(value = "/list",method = RequestMethod.GET)    public String list(Model model) {        // list.jsp + model = modelandview        List list = secKillService.getSecKillList();        model.addAttribute("list",list);        return "list";    }     @RequestMapping(value = "/{seckillId}/detail", method = RequestMethod.GET)    public String detail(@PathVariable("seckillId") Long seckillId, Model model) {        if (seckillId == null) {            // 0. 不存在就重定向到list            // 1. 重定向访问服务器两次            // 2. 重定向可以重定义到任意资源路径。            // 3. 重定向会产生一个新的request,不能共享request域信息与请求参数            return "redrict:/seckill/list";         }        SecKill secKill = secKillService.getById(seckillId);        if (secKill == null) {            // 0. 为了展示效果用forward            // 1. 转发只访问服务器一次。            // 2. 转发只能转发到自己的web应用内            // 3. 转发相当于服务器跳转,相当于方法调用,在执行当前文件的过程中转向执行目标文件,            //      两个文件(当前文件和目标文件)属于同一次请求,前后页 共用一个request,可以通            //      过此来传递一些数据或者session信息            return "forward:/seckill/list";        }        model.addAttribute("seckill",secKill);        return "detail";    }     // ajax json    @RequestMapping(value = "/{seckillId}/exposer",            method = RequestMethod.POST,            produces = {"application/json;charset=UTF8"})    @ResponseBody    public SeckillResult exposer(Long seckillId) {        SeckillResult result;        try {            Exposer exposer = secKillService.exportSecKillUrl(seckillId);            result = new SeckillResult(true,exposer);        } catch (Exception e) {            logger.error(e.getMessage(),e);            result = new SeckillResult<>(false,e.getMessage());        }        return result;    }     @RequestMapping(value = "/{seckillId}/{md5}/execution",        method = RequestMethod.POST,        produces = {"application/json;charset=UTF8"})    public SeckillResult execute(            @PathVariable("seckillId") Long seckillId,            // required = false表示cookie逻辑由我们程序处理,springmvc不要报错            @CookieValue(value = "killPhone",required = false) Long userPhone,            @PathVariable("md5") String md5) {        if (userPhone == null) {            return new SeckillResult(false, "未注册");        }        SeckillResult result;        try {            SeckillExecution execution = secKillService.executeSeckill(seckillId, userPhone, md5);            result = new SeckillResult(true, execution);            return result;        } catch (SeckillCloseException e) { // 秒杀关闭            SeckillExecution execution = new SeckillExecution(seckillId, SecKillStatEnum.END);            return new SeckillResult(false,execution);        } catch (RepeatKillException e) { // 重复秒杀            SeckillExecution execution = new SeckillExecution(seckillId, SecKillStatEnum.REPEAT_KILL);            return new SeckillResult(false,execution);        } catch (Exception e) {            // 不是重复秒杀或秒杀结束,就返回内部错误            logger.error(e.getMessage(), e);            SeckillExecution execution = new SeckillExecution(seckillId, SecKillStatEnum.INNER_ERROR);            return new SeckillResult(false,execution);        }     }     @RequestMapping(value = "/time/now",method = RequestMethod.GET)    @ResponseBody    public SeckillResult time() {        Date now = new Date();        return new SeckillResult(true,now.getTime());    }}

      页面

      这里修改数据库为合适的时间来测试我们的代码。

      点击后跳转到详情页。

      详情页涉及到比较多的交互逻辑,如cookie,秒杀成功失败等等。放到逻辑交互一节来说。

      运行时发现jackson版本出现问题,pom.xml修改为:

        com.fasterxml.jackson.core  jackson-databind  2.10.2

      list.jsp代码为:

      <%@ page contentType="text/html;charset=UTF-8" language="java" %><%--引入jstl--%><%--标签通用头,写在一个具体文件,直接静态包含--%><%@include file="common/tag.jsp"%>      Bootstrap 模板    <%--静态包含:会合并过来放到这,和当前文件一起作为整个输出--%>    <%@include file="common/head.jsp"%>     <%--页面显示部分--%>    

      秒杀列表

      名称 库存 开始时间 结束时间 创建时间 详情页
      ${sk.name} ${sk.number} link

      逻辑交互

      身份认证

      cookie中没有手机号要弹窗,手机号不正确(11位数字)要提示错误:

      选择提交之后要能够在cookie中看到:

      目前为止detail.jsp:

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>      秒杀详情页    <%--静态包含:会合并过来放到这,和当前文件一起作为整个输出--%>    <%@include file="common/head.jsp"%>         <%----%>    

      ${seckill.name}

      <%--开始写交互逻辑--%>

      我们的逻辑主要写在另外的js文件中:

      seckill.js

      // 存放主要交互逻辑js// javascript 模块化var seckill={    // 封装秒杀相关ajax的URL    URL:{     },    // 验证手机号    validatePhone: function (phone) {        if(phone && phone.length==11 && !isNaN(phone)) {            return true;        } else {            return false;        }    },    // 详情页秒杀逻辑    detail: {        // 详情页初始化        init: function (params) {            // 手机验证和登录,计时交互            // 规划交互流程            // 在cookie中查找手机号            var killPhone = $.cookie('killPhone');            var startTime = params['startTime'];            var endTime = params['endTime'];            var seckillId = params['seckillId'];            // 验证手机号            if(!seckill.validatePhone(killPhone)) {                // 绑定手机号,获取弹窗输入手机号的div id                var killPhoneModal = $('#killPhoneModal');                killPhoneModal.modal({                    show: true, //显示弹出层                    backdrop: 'static',//禁止位置关闭                    keyboard: false, //关闭键盘事件                });                $('#killPhoneBtn').click(function () {                    var inputPhone = $('#killphoneKey').val();                    // 输入格式什么的ok了就刷新页面                    if(seckill.validatePhone(inputPhone)) {                        // 将电话写入cookie                        $.cookie('killPhone',inputPhone,{expires:7,path:'/seckill'});                        _window.location.reload();                    } else {                        // 更好的方式是把字符串写入字典再用                        $('#killphoneMessage').hide().html('').show(500);                    }                });            }            // 已经登录         }    }}

      计时面板

      在登录完成后,处理计时操作:

      // 已经登录// 计时交互$.get(seckill.URL.now(),{},function (result) {    if(result && result['success']) {        var nowTime = result['data'];        // 写到函数里处理        seckill.countdown(seckillId,nowTime,startTime,endTime);    } else {        console.log('result: '+result);    }});

      在countdown函数里,有三个判断,未开始、已经开始、结束。

      URL:{    now: function () {        return '/seckill/time/now';    }}, handleSeckill: function () {    // 处理秒杀逻辑}, countdown: function (seckillId,nowTime,startTime,endTime) {    var seckillBox = $('#seckillBox');    if(nowTime>endTime) {        seckillBox.html('秒杀结束!');    } else  if(nowTime ### 秒杀交互  秒杀之前: ![image-20211006202253376](https://img-blog.csdnimg.cn/img_convert/7609c513cb3b64f4e710d879e57c1651.png) 详情页: image-20211006201149488 点击开始秒杀: image-20211006202320137 列表页刷新: ![image-20211006202306300](https://img-blog.csdnimg.cn/img_convert/272dac0d7f6d4a2910614551f4580aac.png)  运行时发现controller忘了写`@ResponseBody`了,这里返回的不是jsp是json,需要加上。 ```java@ResponseBodypublic SeckillResult execute(        @PathVariable("seckillId") Long seckillId,        // required = false表示cookie逻辑由我们程序处理,springmvc不要报错        @CookieValue(value = "killPhone",required = false) Long userPhone,        @PathVariable("md5") String md5)

      在seckill.js中,补全秒杀逻辑:

      // 封装秒杀相关ajax的URLURL:{    now: function () {        return '/seckill/time/now';    },    exposer: function(seckillId) {        return '/seckill/'+seckillId+'/exposer';    },     execution: function (seckillId,md5) {        return '/seckill/'+seckillId+'/'+md5+'/execution';    }}, // id和显示计时的那个模块handleSeckill: function (seckillId,node) {    // 处理秒杀逻辑    // 在计时的地方显示一个秒杀按钮    node.hide()        .html('');    // 获取秒杀地址    $.post(seckill.URL.exposer(),{seckillId},function (result) {        if(result && result['success']) {            var exposer = result['data'];            if(exposer['exposed']) {                // 如果开启了秒杀                // 获取秒杀地址                var md5 = exposer['md5'];                var killUrl = seckill.URL.execution(seckillId,md5);                console.log("killurl: "+killUrl);               // click永远绑定,one只绑定一次                $('#killBtn').one('click',function () {                    // 执行秒杀请求操作                    // 先禁用按钮                    $(this).addClass('disabled');                    // 发送秒杀请求                    $.post(killUrl,{},function (result) {                        if(result) {                             var killResult = result['data'];                            var state = killResult['state'];                            var stateInfo = killResult['stateInfo'];                            // 显示秒杀结果                            if(result['success']) {                                node.html(''+stateInfo+'');                            } else {                                node.html(''+stateInfo+'');                            }                         }                        console.log(result);                    })                });                node.show();            } else {                // 未开始秒杀,这里是因为本机显示时间和服务器时间不一致                // 可能浏览器认为开始了,服务器其实还没开始                var now = exposer['now'];                var start = exposer['start'];                var end = exposer['end'];                // 重新进入倒计时逻辑                seckill.countdown(seckillId,now,start,end);            }        } else {            console.log('result='+result);        }    })},

      秒杀成功后再次进行秒杀则不成功:

      输出:

      在库存不够时也返回秒杀结束:

      至此,功能方面已经实现了,后面还剩下优化部分。

      看完上述内容,你们对Java中SpringMVC的使用方法有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注行业资讯频道,感谢大家的支持。

      0