(三)基于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 = Class());
@ResponseBody
@RequestMapping("/findAll")
public List<Seckill>findAll(){
return seckillService.findAll();
}
jquery框架定义@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 = portSeckillUrl(seckillId);
result =new SeckillResult<Exposer>(true, exposer);
}catch(Exception e){
<(e.getMessage(), e);
result =new SeckillResult<Exposer>(false, e.getMessage());
}
return result;
}
@RequestMapping(value ="/{seckillId}/{md5}/execution",
@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 = uteSeckill(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, Time());
}
}
下⾯我以问答的形式讲解⼀下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;
< = 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层开发已经⼤功告成了!
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论