基于redis实现未登录购物车
- springboot 工程
- 主要说明购物车流程(故将登录用户信息保存至session)
- 未登录时 将用户临时key 保存至cookie
- 有不足之处 请大佬指点
项目源码: https://github.com/youxiu326/sb_shopping_cart
项目结构:
package com.youxiu326.common;
public class JsonResult {
private String code;
private String message;
private Object data;
public JsonResult() {
this.code = "200";
this.message = "操作成功";
}
public JsonResult success(String message){
this.code = "200";
this.message = message;
return this;
}
public JsonResult error(String message){
this.code = "400";
this.message = message;
return this;
}
public JsonResult error(){
this.code = "400";
this.message = "操作失败";
return this;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
JsonResult
package com.youxiu326.config; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import javax.annotation.PostConstruct; /**
* 防止redis 中文乱码
*/
@Configuration
public class RedisConfig { @Autowired
private RedisTemplate<Object, Object> redisTemplate; @PostConstruct
public void initRedisTemplate() {
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
} }
RedisConfig.java
package com.youxiu326.entity; import java.io.Serializable; /**
* 用户
*/
public class Account implements Serializable { private String id = "youxiu326"; private String code = "test"; private String pwd = "123456"; public String getId() {
return id;
} public void setId(String id) {
this.id = id;
} public String getCode() {
return code;
} public void setCode(String code) {
this.code = code;
} public String getPwd() {
return pwd;
} public void setPwd(String pwd) {
this.pwd = pwd;
}
}
Account.java
package com.youxiu326.entity; import java.io.Serializable;
import java.util.ArrayList;
import java.util.List; /**
* 购物车对象 一个购物车由n个CartItem组成
*/
public class ShoppingCart implements Serializable { public static final String unLoginKeyPrefix="TMP_"; public static final String loginKeyPrefix="USER_"; private String key=""; private List<CartItem> cartItems = new ArrayList<>();//防止空指针 public ShoppingCart(){} public ShoppingCart(String key) {
this.key = key;
} public String getKey() {
return key;
} public void setKey(String key) {
this.key = key;
} public List<CartItem> getCartItems() {
return cartItems;
} public void setCartItems(List<CartItem> cartItems) {
this.cartItems = cartItems;
}
}
ShoppingCart.java
package com.youxiu326.entity; import java.io.InputStream;
import java.io.Serializable;
import java.util.Objects; /**
* 购物实体
*/
public class CartItem implements Serializable { private String code; private Integer quantity; public String getCode() {
return code;
} public void setCode(String code) {
this.code = code;
} public Integer getQuantity() {
return quantity;
} public void setQuantity(Integer quantity) {
this.quantity = quantity;
} @Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CartItem cartItem = (CartItem) o;
return Objects.equals(code, cartItem.code);
} @Override
public int hashCode() {
return Objects.hash(code);
}
}
CartItem.java
# post
server.port=8888 # redis
spring.redis.host=youxiu326.xin
spring.redis.port=6379 # thymeleaf
spring.thymeleaf.cache=false
application.properties
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<base th:href="${#httpServletRequest.getContextPath()+'/'}">
<meta charset="UTF-8">
<title>测试未登陆基于redis实现购物车功能</title>
</head>
<body>
<h2>商品列表</h2> <table>
<tr>
<td>商品编号</td>
<td>商品数量</td>
</tr>
<tr th:each="cart,iterStat : ${cartItems}">
<th scope="row" th:text="${cart.code}">1</th>
<td th:text="${cart.quantity}">quantity</td>
<!-- <td >-->
<!-- <img th:src="${cart.webLogo}"/>-->
<!-- </td>-->
<!-- <td th:text="${iterStat.index}">index</td>-->
</tr>
</table> <br/> <form action="#" method="post">
<table>
<tr>
<td>商品编号:</td>
<td><input type="text" id="code" name="code" value="youxiu001"></td>
</tr>
<tr>
<td>数量:</td>
<td><input id="quantity" name="quantity" value="1"></td>
</tr>
<tr>
<td colspan="1"><button type="button" onclick="add()">加购</button></td>
<td colspan="1"><button type="button" onclick="remove()">减购</button></td>
</tr>
</table>
</form> </body> <script src="/jquery-1.11.3.min.js"></script>
<!--<script th:src="@{/jquery-1.11.3.min.js}"></script>-->
<script>
function add(){
$.ajax({
type: 'POST',
url: "/shopping/add",
data: {"code":$("#code").val(),"quantity":$("#quantity").val()},
// dataType: "json",
success: function(response){
if(response.code=="200"){
window.location.reload();
}else{
alert(response.message);
}
},
error:function(response){
alert(response.message);
console.log(response);
}
});
} function remove(){
$.ajax({
type: 'POST',
url: "/shopping/remove",
data: {"code":$("#code").val(),"quantity":$("#quantity").val()},
// dataType: "json",
success: function(response){
if(response.code=="200"){
window.location.reload();
}else{
alert(response.message);
}
},
error:function(response){
alert("失败");
console.log(response);
}
});
} </script> </html>
index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<base th:href="${#httpServletRequest.getContextPath()+'/'}">
<meta charset="UTF-8">
<title>登陆界面</title>
</head>
<body> <div th:if="${session.account == null}">[未登陆]</div>
<div th:if="${session.account != null}">
<div th:text="${session.account.code}"></div>
</div> <form action="#" method="post">
<table>
<tr>
<td>用户:</td>
<td><input type="text" id="code" name="code" value="test"></td>
</tr>
<tr>
<td>密码:</td>
<td><input id="pwd" name="pwd" value="youxiu326"></td>
</tr>
<tr>
<td colspan="1"><button type="button" onclick="login()">登陆</button></td>
<td colspan="1"><button type="button" onclick="logout()">登出</button></td>
</tr>
</table>
</form> <br/> <a target="_blank" href="/shopping/index">去购物车页面</a> </body> <script src="/jquery-1.11.3.min.js"></script>
<!--<script th:src="@{/jquery-1.11.3.min.js}"></script>-->
<script>
function login(){
$.ajax({
type: 'POST',
url: "/login",
data: {"code":$("#code").val(),"pwd":$("#pwd").val()},
// dataType: "json",
success: function(response){
if(response.code=="200"){
alert(response.message);
window.location.reload();
}else{
alert(response.message);
}
},
error:function(response){
alert(response.message);
console.log(response);
}
});
} function logout(){
$.ajax({
type: 'POST',
url: "/logout",
data: {"code":$("#code").val()},
// dataType: "json",
success: function(response){
if(response.code=="200"){
alert(response.message);
window.location.reload();
}else{
alert(response.message);
}
},
error:function(response){
alert("失败");
console.log(response);
}
});
} </script> </html>
login.html
package com.youxiu326.controller; import com.youxiu326.common.JsonResult;
import com.youxiu326.entity.Account;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; /**
* 登陆接口
*/
@Controller
public class LoginCtrl { @GetMapping("/")
public String toLogin(HttpServletRequest req){
return "login";
} @PostMapping("/login")
@ResponseBody
public JsonResult login(HttpServletRequest req,Account account){ JsonResult result = new JsonResult(); if (StringUtils.isBlank(account.getCode()) || StringUtils.isBlank(account.getPwd())){
result.error("账户或密码为空");
return result;
}
//创建登陆用户账户 【主要逻辑是购物车 登陆用户id 就是用户code】
account.setId(account.getCode());
//将用户保存至session
req.getSession().setAttribute("account",account); return result.success("登陆成功");
} @PostMapping("/logout")
@ResponseBody
public JsonResult logout(HttpServletRequest req,Account account){ JsonResult result = new JsonResult(); if (StringUtils.isBlank(account.getCode())){
result.error("账户为空");
return result;
}
req.getSession().removeAttribute("account"); return result.success("登出成功");
} }
LoginCtrl.java
主要业务流程service:
package com.youxiu326.service.impl; import com.youxiu326.common.JsonResult;
import com.youxiu326.entity.Account;
import com.youxiu326.entity.CartItem;
import com.youxiu326.entity.ShoppingCart;
import com.youxiu326.service.ShoppingCartService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.util.WebUtils;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID; @Service
public class ShoppingCartServiceImpl implements ShoppingCartService { @Autowired
private RedisTemplate redisTemplate; /**
* 获得用户key
*
* 1.用户未登录情况下第一次进入购物车 -> 生成key 保存至cookie中
* 2.用户未登录情况下第n进入购物车 -> 从cookie中取出key
* 3.用户登录情况下 -> 根据用户code生成key
* 4.用户登录情况下并且cookie中存在key-> 从cookie取的的key从缓存取得购物车 合并至
* 用户code生成key的购物车中去 ,这样后面才能根据用户code 取得正确的购物车
*
* @param req
* @param resp
* @param account
* @return
*/
@Override
public String getKey(HttpServletRequest req, HttpServletResponse resp, Account account) {
//https://github.com/youxiu326/sb_shiro_session.git String key = null; //最终返回的key
String tempKey = ""; //用来存储cookie中的临时key, Cookie cartCookie = WebUtils.getCookie(req, "shoopingCart");
if(cartCookie!=null){
//获取Cookie中的key
key = cartCookie.getValue();
tempKey = cartCookie.getValue();
}
if(StringUtils.isBlank(key)){
key = ShoppingCart.unLoginKeyPrefix + UUID.randomUUID();
if (account!=null)
key = ShoppingCart.loginKeyPrefix + account.getId();
Cookie cookie = new Cookie("shoopingCart",key);
cookie.setMaxAge(-1);
cookie.setPath("/");
resp.addCookie(cookie);
}else if (StringUtils.isNotBlank(key) && account!=null){//⑵
key = ShoppingCart.loginKeyPrefix + account.getId();
if (tempKey.startsWith(ShoppingCart.unLoginKeyPrefix)){//⑴
//1.满足cookie中取得的key 为未登录时的key
//2.满足当前用户已经登录
//3.合并未登录时用户所添加的购物车商品⑷
mergeCart(tempKey,account);//⑶
}
}
return key;
} /**
* 合并购物车 返回最终购物车
* @param tempKey
*/
public ShoppingCart mergeCart(String tempKey,Account account) { ShoppingCart loginCart = null;
String loginkey = null; // 从redis取出用户缓存购物车数据
HashOperations<String, String, ShoppingCart> vos = redisTemplate.opsForHash();
ShoppingCart unLoginCart = vos.get("CACHE_SHOPPINGCART", tempKey);
if (unLoginCart == null){
unLoginCart = new ShoppingCart(tempKey);
}
if (account != null && tempKey.startsWith(ShoppingCart.unLoginKeyPrefix)) {//⑵
//如果用户登录 并且 当前是未登录的key
loginkey = ShoppingCart.loginKeyPrefix + account.getId();
loginCart = mergeCart(loginkey, account);
if (null != unLoginCart.getCartItems()) {//⑴ if (null != loginCart.getCartItems()) {
//满足未登录时的购物车不为空 并且 当前用户已经登录
//进行购物车合并
for (CartItem cv : unLoginCart.getCartItems()) {
long count = loginCart.getCartItems().stream().filter(it->it.getCode().equals(cv.getCode())).count();
if(count == 0 ){//没有重复的商品 则直接将商品加入购物车
loginCart.getCartItems().add(cv);
}else if(count == 1){//出现重复商品 修改数量
CartItem c = loginCart.getCartItems().stream().filter(it->it.getCode().equals(cv.getCode())).findFirst().orElse(null);
c.setQuantity(c.getQuantity()+1);
}
}
} else {
//如果当前登录用户的购物车为空则 将未登录时的购物车合并
loginCart.setCartItems(unLoginCart.getCartItems());
}
unLoginCart = loginCart;
//【删除临时key】
vos.delete("CACHE_SHOPPINGCART",tempKey);
//【将合并后的购物车数据 放入loginKey】//TMP_4369f86d-c026-4b1b-8fec-f3c69f6ffac5
vos.put("CACHE_SHOPPINGCART",loginkey, unLoginCart);
}
} return unLoginCart;
} /**
* 添加购物车
* @param req
* @param resp
* @param account 登陆用户信息
* @param item 添加的购物车商品信息 包含商品code 商品加购数量
* @return
*/
public JsonResult addCart(HttpServletRequest req, HttpServletResponse resp,Account account,CartItem item){
JsonResult result = new JsonResult();
String key = getKey(req, resp,account);//得到最终key
ShoppingCart cacheCart = mergeCart(key,account);//根据key取得最终购物车对象
if(StringUtils.isNotBlank(item.getCode()) && item.getQuantity()>0){
//TODO 进行一系列 商品上架 商品code是否正确 最大购买数量....
if(false){
return result.error();
}
long count = 0;
if(null != cacheCart.getCartItems()) {
count = cacheCart.getCartItems().stream().filter(it->it.getCode().equals(item.getCode())).count();
}
if (count==0){
//之前购物车无该商品记录 则直接添加
cacheCart.getCartItems().add(item);
}else {
//否则将同一商品数量相加
CartItem c = cacheCart.getCartItems().stream().filter(it->it.getCode().equals(item.getCode())).findFirst().orElse(null);
c.setQuantity(c.getQuantity()+item.getQuantity()); }
}
//【将合并后的购物车数据 放入loginKey】
HashOperations<String,String,ShoppingCart> vos = redisTemplate.opsForHash();
vos.put("CACHE_SHOPPINGCART",key, cacheCart);
result.setData(cacheCart);
return result;
} /**
* 移除购物车
* @param req
* @param resp
* @param account
* @param item
* @return
*/
public JsonResult removeCart(HttpServletRequest req, HttpServletResponse resp,Account account,CartItem item){
JsonResult result = new JsonResult();
String key = getKey(req, resp,account);//得到最终key
ShoppingCart cacheCart = mergeCart(key , account);//根据key取得最终购物车对象
if(cacheCart!=null && cacheCart.getCartItems()!=null && cacheCart.getCartItems().size()>0){//⑴
//
long count = cacheCart.getCartItems().stream().filter(it->it.getCode().equals(item.getCode())).count();
if(count == 1 ){//⑵
CartItem ci = cacheCart.getCartItems().stream().filter(it->it.getCode().equals(item.getCode())).findFirst().orElse(null);
if (ci.getQuantity()>item.getQuantity()){//⑶
ci.setQuantity(ci.getQuantity()-item.getQuantity());
}else if(ci.getQuantity()<=item.getQuantity()){
cacheCart.getCartItems().remove(ci);
}
//1.满足缓存购物车中必须有商品才能减购物车
//2.满足缓存购物车中有该商品才能减购物车
//3.判断此次要减数量是否大于缓存购物车中数量 进行移除还是数量相减操作
}
HashOperations<String,String,ShoppingCart> vos = redisTemplate.opsForHash();
vos.put("CACHE_SHOPPINGCART",key, cacheCart);
}
result.setData(cacheCart);
return result;
} /**
* 【场景:我加购了一双40码的鞋子到购物车 现在我想换成41码的鞋子】
* 【例如:原商品code ABCDEFG40 -> ABCDEFG41】
*
* @param req
* @param resp
* @param account
* @param item 新购物商品
* @param oldItem 原购物商品
* @return
*/
public String updateCart(HttpServletRequest req, HttpServletResponse resp,Account account,CartItem item,CartItem oldItem){ //TODO 校验商品信息是否合法 是否上架 库存 最大购买数量....
if(false){
return null;
} String key = getKey(req, resp,account);
ShoppingCart cacheCart = mergeCart(key , account);//TODO 待探讨
cacheCart.getCartItems().remove(item);
cacheCart.getCartItems().remove(oldItem);
cacheCart.getCartItems().add(oldItem);
HashOperations<String,String,ShoppingCart> vos = redisTemplate.opsForHash();
vos.put("CACHE_SHOPPINGCART",key, cacheCart);
return null;
} }
controller调用:
package com.youxiu326.controller; import com.youxiu326.common.JsonResult;
import com.youxiu326.entity.Account;
import com.youxiu326.entity.CartItem;
import com.youxiu326.entity.ShoppingCart;
import com.youxiu326.service.ShoppingCartService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.util.WebUtils;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; /**
* 购物车 Controller
*/
@Controller
@RequestMapping("/shopping")
public class ShoppingCartCtrl { private final Logger LOGGER = LoggerFactory.getLogger(this.getClass()); private Account account; @Autowired
private ShoppingCartService service; /**
* 进入首页
* @return
*/
@GetMapping("/index")
public String toIndex(HttpServletRequest req, HttpServletResponse resp, Model model){ account = (Account) req.getSession().getAttribute("account"); String key = service.getKey(req, resp, this.account);
ShoppingCart cacheCart = service.mergeCart(key, this.account);
model.addAttribute("cartItems",cacheCart.getCartItems()); return "index";
} @PostMapping("/add")
@ResponseBody
public JsonResult add(HttpServletRequest req, HttpServletResponse resp, CartItem cartItem){
account = (Account) req.getSession().getAttribute("account");
JsonResult result = service.addCart(req, resp, account, cartItem);
return result;
} @PostMapping("/remove")
@ResponseBody
public JsonResult remove(HttpServletRequest req, HttpServletResponse resp, CartItem cartItem){
account = (Account) req.getSession().getAttribute("account");
JsonResult result = service.removeCart(req, resp, account, cartItem);
return result;
} @PostMapping("/update")
@ResponseBody
public String update(HttpServletRequest req, HttpServletResponse resp){ account = (Account) req.getSession().getAttribute("account"); return "";
} }
演示效果:
项目源码: https://github.com/youxiu326/sb_shopping_cart
基于redis实现未登录购物车的更多相关文章
- springcloud微服务基于redis集群的单点登录
springcloud微服务基于redis集群的单点登录 yls 2019-9-23 简介 本文介绍微服务架构中如何实现单点登录功能 创建三个服务: 操作redis集群的服务,用于多个服务之间共享数据 ...
- .NET基于Redis缓存实现单点登录SSO的解决方案[转]
一.基本概念 最近公司的多个业务系统要统一整合使用同一个登录,这就是我们耳熟能详的单点登录,现在就NET基于Redis缓存实现单点登录做一个简单的分享. 单点登录(Single Sign On),简称 ...
- .NET基于Redis缓存实现单点登录SSO的解决方案
一.基本概念 最近公司的多个业务系统要统一整合使用同一个登录,这就是我们耳熟能详的单点登录,现在就NET基于Redis缓存实现单点登录做一个简单的分享. 单点登录(Single Sign On),简称 ...
- 基于Redis位图实现系统用户登录统计
项目需求,试着写了一个简单登录统计,基本功能都实现了,日志数据量小.具体性能没有进行测试~ 记录下开发过程与代码,留着以后改进! 1. 需求 1. 实现记录用户哪天进行了登录,每天只记录是否登录过,重 ...
- 基于Redis主从复制读写分离架构的Session共享
1.搭建主从复制 第一步:将Redis拷贝到虚拟机上的指定文件夹内,此Redis作为主服务 第二步:将Redis拷贝到本机的指定文件夹内,此Redis作为从服务 第三步:修改主服务的配置文件(redi ...
- 基于Redis主从复制读写分离架构的Session共享(Windows Server)
搭建主从复制 1.安装软件 下载Redis-x64-3.2.100.zip:https://github.com/MicrosoftArchive/redis/releases 第一步:将Redis拷 ...
- Spring+Shiro搭建基于Redis的分布式权限系统(有实例)
摘要: 简单介绍使用Spring+Shiro搭建基于Redis的分布式权限系统. 这篇主要介绍Shiro如何与redis结合搭建分布式权限系统,至于如何使用和配置Shiro就不多说了.完整实例下载地址 ...
- 基于redis分布式缓存实现(新浪微博案例)
第一:Redis 是什么? Redis是基于内存.可持久化的日志型.Key-Value数据库 高性能存储系统,并提供多种语言的API. 第二:出现背景 数据结构(Data Structure)需求越来 ...
- 项目分布式部署那些事(1):ONS消息队列、基于Redis的Session共享,开源共享
因业务发展需要现在的系统不足以支撑现在的用户量,于是我们在一周之前着手项目的性能优化与分布式部署的相关动作. 概况 现在的系统是基于RabbitHub(一套开源的开发时框架)和Rabbit.WeiXi ...
随机推荐
- Python:获取某一月的天数
import calendarcalendar.monthlen(2021,6)30calendar.monthrange(2021,6)(1, 30) calendar.monthrange( ye ...
- base64学习笔记
关于是什么: 定义 8Bits字节编码方式之一 应用 传输8Bit字节代码 特性 Base64编码具有不可读性 描述 Base64可以成为密码学的基石,非常重要. 特点 可以将任意的二进制数据进行Ba ...
- tp5 ajax单文件上传
HTML代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset="U ...
- Laravel-Auth认证
1.建立数据表 admins 2.找到config下的auth.php 文件,加入如下代码 <?php return [ /* |-------------------------------- ...
- phpStudy 升级 MySQL5.7
最新在开发项目中需要使用到mysql5.7以上版本,但是phpStudy的版本是5.5,所以需要针对MySQL升级一下 步骤 1.备份原本MySQL 备份:原本phpStudy中的MySQL文件夹改 ...
- 分析vue脚手架
执行流程: 执行npm run serve.找到了main.js文件,之后引入Vue.App等等.后来找到App组件,发现里面用到了组件School,于是执行School组件,最终汇总到App组件.通 ...
- ubuntu 学习中的坑-2021-11-22
安装ssh-server服务 查看是否安装ssh服务 #dpkg -l | grep ssh 安装ssh-server服务 #sudo apt-get install openssh-server 然 ...
- 变量、变量作用域、常量final、变量的命名规范
变量 变量是什么:就是可以变化的量! Java是一种强类型语言,每个变量都必须声明其类型. Java变量是程序中最基本的存储单元,其要素包括变量名,变量类型和作用域. 注意事项: 每个变量都有类型,类 ...
- 【译】.NET 6 网络改进
原文 | Máňa Píchová 翻译 | 郑子铭 对于 .NET 的每个新版本,我们都希望发布一篇博客文章,重点介绍网络的一些变化和改进.在这篇文章中,我很高兴谈论 .NET 6 中的变化. 这篇 ...
- 《手把手教你》系列基础篇(八十)-java+ selenium自动化测试-框架设计基础-TestNG依赖测试-番外篇(详解教程)
1.简介 经过前边几篇知识点的介绍,今天宏哥就在实际测试中应用一下前边所学的依赖测试.这一篇主要介绍在TestNG中一个类中有多个测试方法的时候,多个测试方法的执行顺序或者依赖关系的问题.如果不用de ...