(三)基于SpringBoot实战Java秒杀高并发系统Controller层开发(超详细)
基于上一篇的文章我们继续来探讨,今天我们开始讲SpringBoot实现Java高并发秒杀系统之Controller层开发。
Controller层即是控制层,当然我们所说的都是在基于Spring框架的系统上而言的,传统的SSM项目中,与页面进行交互的是SpringMVC框架,因spring+springmvc+mybatis配置比较繁琐,而且需要配置大量的XML文件,非常不利于后来就被Springboot给代替了,SpringMVC框架在与页面的交互上提供了更加便捷的方式,MVC的设计模式也是当前非常流行的一种设计模式。这次我们针对秒杀系统讲解一下秒杀系统需要和页面交互的操作和数据都涉及哪些?
前端页码设计
编写Controller就是要搞清楚:1.页面需要什么数据?2.页面将返回给Controller什么数据?3.Controller应该返回给页面什么数据?
带着这些问题我们看一下秒杀详情页流程逻辑(不再讲基本的findById和findAll()方法):
因为整个秒杀系统中最核心的业务就是:1.减库存;2.查询订单明细。我们看一下Controller层的源码:
@Controller @RequestMapping("/seckill") public class SeckillController { @Autowired private SeckillService seckillService; private final Logger logger = LoggerFactory.getLogger(this.getClass()); @ResponseBody @RequestMapping("/findAll") public List<Seckill> findAll() { return seckillService.findAll(); } @ResponseBody @RequestMapping("/findById") public Seckill findById(@RequestParam("id") Long id) { return seckillService.findById(id); } @RequestMapping("/{seckillId}/detail") public String detail(@PathVariable("seckillId") Long seckillId, Model model) { if (seckillId == null) { return "page/seckill"; } Seckill seckill = seckillService.findById(seckillId); model.addAttribute("seckill", seckill); if (seckill == null) { return "page/seckill"; } return "page/seckill_detail"; } @ResponseBody @RequestMapping(value = "/{seckillId}/exposer", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8"}) public SeckillResult<Exposer> exposer(@PathVariable("seckillId") Long seckillId) { SeckillResult<Exposer> result; try { Exposer exposer = seckillService.exportSeckillUrl(seckillId); result = new SeckillResult<Exposer>(true, exposer); } catch (Exception e) { logger.error(e.getMessage(), e); result = new SeckillResult<Exposer>(false, e.getMessage()); } return result; } @RequestMapping(value = "/{seckillId}/{md5}/execution", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8"}) @ResponseBody public SeckillResult<SeckillExecution> execute(@PathVariable("seckillId") Long seckillId, @PathVariable("md5") String md5, @RequestParam("money") BigDecimal money, @CookieValue(value = "killPhone", required = false) Long userPhone) { if (userPhone == null) { return new SeckillResult<SeckillExecution>(false, "未注册"); } try { SeckillExecution execution = seckillService.executeSeckill(seckillId, money, userPhone, md5); return new SeckillResult<SeckillExecution>(true, execution); } catch (RepeatKillException e) { SeckillExecution seckillExecution = new SeckillExecution(seckillId, SeckillStatEnum.REPEAT_KILL); return new SeckillResult<SeckillExecution>(true, seckillExecution); } catch (SeckillCloseException e) { SeckillExecution seckillExecution = new SeckillExecution(seckillId, SeckillStatEnum.END); return new SeckillResult<SeckillExecution>(true, seckillExecution); } catch (SeckillException e) { SeckillExecution seckillExecution = new SeckillExecution(seckillId, SeckillStatEnum.INNER_ERROR); return new SeckillResult<SeckillExecution>(true, seckillExecution); } } @ResponseBody @GetMapping(value = "/time/now") public SeckillResult<Long> time() { Date now = new Date(); return new SeckillResult(true, now.getTime()); } }
下面我以问答的形式讲解一下Controller层方法的定义:
1.@ResponseBody和@RestController注解分别有什么作用?
@ResponseBody注解标识的方法,Spring会将此方法return的数据转换成JSON格式且不会被Spring视图解析器所扫描到,也就是此方法永不可能返回一个视图页面。且这个注解只能用在方法体上,不能用在类上。
@RestController注解标识的类,Spring会将其下的所有方法return的数据都转换成JSON格式且不会被Spring视图解析器扫描到,也就是此类下面的所有方法都不可能返回一个视图页面。且这个注解只能用在类上,不能用在方法体上。
2.@RequestMapping中{xx}的语法是什么?@PathVariable注解的用处是什么?
Spring框架很早就支持开发REST资源。也是就是现在我们定义的RESTful URL,在Spring框架上支持的尤为完美,我们可以在Controller中定义这样一个URL映射地址:/{id}/detail,他是合理的RESTful URL定义方式。
这种URL的特点:URL地址由动态的数据拼接组成的,而不是将所有的资源全部映射到一个路径下,比如:/article/detail。
这种URL结构的优势:我们能很容易从URL地址上判断出该地址所展示的页面是什么?比如:/1/detail就可能表示ID为1的文章的详情页,看起来设计的很清晰。
这种URL如何进行交互:我们定义了/{id}/detail这样一个URL映射地址,其对应的映射方法上就应该添加@PathVariable注解标识,如:@PathVariable(“id”) Long idSpring就能装配前端传递的URL中指定位置的数据并赋值给id这个参数。比如前端调用后端接口:localhost:8080/seckill/1/detail,后端存在一个映射方法:@RequestMapping("/{id}/detail"),这样就能刚好匹配上这个URL映射地址。
所以我们看一下秒杀系统的RESTful URL设计:
3.为什么要单独写一个接口用来获取当前系统时间?
由于我们开发的系统肯定不是给自己用的,我们的用户可能处于不同的时区,他们的当前系统时间也是不同的,所以我们写一个通用的时间规范:就是当前服务器的时间。
4.SeckillResult是什么?
在前面我们将Service层系统开发的时候就手动创建了很多类来封装一些通用的结果信息。而对于Controller层也会返回很多结果数据,比如传入的URL中id值为null,那么就没必要继续向下请求,而是直接给页面返回false信息。
于是我们创建:SeckillResult.java
public class SeckillResult<T> { private boolean success; private T data; private String error; public SeckillResult(boolean success, T data) { this.success = success; this.data = data; } public SeckillResult(boolean success, String error) { this.success = success; this.error = error; } }
泛型T表示可以代表不同类型的对象。这是泛型类应用很广泛的一个特性,我们调用SeckillResult类,将其中的T用什么替换那么T就表示这个替换的对象类型。
页面设计用了哪些技术?
HTML页面,用Bootstrap绘制。
Thymeleaf模板引擎渲染HTML页面,使得HTML页面拥有类似JSP页面一样功能。
JS方面使用原生的JQuery。
本项目使用Cookie存储用户手机号的方式模拟用户登录功能,实际上没有与后端交互的操作。如果用户没有登录就打开了商品详情页会直接弹出一个手机号登录框提醒用户登录,且没有登录时无法关闭登录框的。
思考
在从JSP页面转换到HTML页面的时候我常会遇到这么一个问题:前端如何取出来后端查询到的数据?
在之前我们写的JSP页面中,可以通过将后端查询到的数据放进request,session域对象中,JSP页面可以直接调用Java中域对象的数据,甚至可以通过EL表达式(${})来直接获取参数,但是这种方法有一个弊端:Controller必须是返回一个视图,这样才能在此视图中获取存进域对象中的数据。
而我们现在都开始用HTML页面,也无法从域对象中取出数据该怎么办呢?我这里提供两个思路:
1.像本项目中一样,前端使用Thymeleaf模板引擎渲染页面,那么Thymeleaf内置很多方法如同JSP页面的EL表达式。Thymeleaf在HTML中取出域对象数据使用:;在JS中取出域对象数据:var v = [[${xx}]](当然都必须是在HTML页面中,在外部JS文件中是得不到数据的)。
2.使用原生js提供的location对象,我们先看一下URL的组成结构:
举个例子
function QueryUrl(name){ var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)"); var r = window.location.search.substr(1).match(reg); if(r!=null)return unescape(r[2]); return null; } // 调用方法 alert(QueryUrl("参数名1"));
自此,controller层开发已经大功告成了!
- Java 开发基于Zookeeper,Spring,vue.js的高并发多用户模块化微信商城系统(四) Java微框架Spring Boot的应用
- 最新基于SpringBoot Java商城秒杀系统的设计与实战
- Java秒杀系统实战系列~构建SpringBoot多模块项目
- 基于Spring Boot框架企业级应用系统开发全面实战(Spring Boot,Spring MVC,Maven,bootstrap,angular js)
- 【SpringBoot商城秒杀系统项目实战20】高并发秒杀接口优化
- SpringBoot+Redis实现实现Java高并发之秒杀系统
- Java 开发基于Zookeeper,Spring,vue.js的高并发多用户模块化微信商城系统(二) Zookeeper原理
- Java 开发基于Zookeeper,Spring,vue.js的高并发多用户模块化微信商城系统(三) 构建高可用MySQL服务
- 基于Spring Boot框架企业级应用系统开发全面实战
- Java 开发基于Zookeeper,Spring,vue.js的高并发多用户模块化微信商城系统(一) 项目介绍
- 【SpringBoot商城秒杀系统项目实战21】高并发秒杀系统接口优化 RabbitMQ异步下单
- 基于SpringBoot实现高并发商品秒杀系统
- Java秒杀系统方案优化 高性能高并发实战视频
- 基于springboot+redis+bootstrap+mysql开发一套属于自己的分布式springcloud云权限架构(十七)【权限架构系统(基础框架搭建)】
- Java Web整合开发实战:基于Struts 2+Hibernate+Spring 目录
- Java Spring boot 企业微信点餐系统 项目实战视频教程
- 【SpringBoot商城秒杀系统项目实战03】集成Thymeleaf做页面模板
- Java秒杀系统方案优化,高性能高并发实战
- 优效学院 基于微服务的秒杀项目实战 Spring Boot 2.0基础篇01
- 2018最全Java秒杀系统方案优化 高性能高并发实战教程