1、通用的结果处理,及错误信息类封装
2、md5前台后台两次加盐加密
3、全局异常处理
4、通用的key生成策略
5、通用的RedisService方法
通用的结果处理,及错误信息类封装
写一个Result类来封装返回结果,避免重复性的代码出现到controller层
package com.javaxl.miaosha_02.result; public class Result<T> { private int code; private String msg; private T data; /** * 成功时候的调用 * */ public static <T> Result<T> success(T data){ return new Result<T>(data); } /** * 失败时候的调用 * */ public static <T> Result<T> error(CodeMsg codeMsg){ return new Result<T>(codeMsg); } private Result(T data) { this.data = data; } private Result(int code, String msg) { this.code = code; this.msg = msg; } private Result(CodeMsg codeMsg) { if(codeMsg != null) { this.code = codeMsg.getCode(); this.msg = codeMsg.getMsg(); } } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public T getData() { return data; } public void setData(T data) { this.data = data; } }
CodeMsg:对系统常量进行封装
package com.javaxl.miaosha_02.result; public class CodeMsg { private int code; private String msg; //通用的错误码 public static CodeMsg SUCCESS = new CodeMsg(0, "success"); public static CodeMsg SERVER_ERROR = new CodeMsg(500100, "服务端异常"); public static CodeMsg BIND_ERROR = new CodeMsg(500101, "参数校验异常:%s"); //登录模块 5002XX public static CodeMsg SESSION_ERROR = new CodeMsg(500210, "Session不存在或者已经失效"); public static CodeMsg PASSWORD_EMPTY = new CodeMsg(500211, "登录密码不能为空"); public static CodeMsg MOBILE_EMPTY = new CodeMsg(500212, "手机号不能为空"); public static CodeMsg MOBILE_ERROR = new CodeMsg(500213, "手机号格式错误"); public static CodeMsg MOBILE_NOT_EXIST = new CodeMsg(500214, "手机号不存在"); public static CodeMsg PASSWORD_ERROR = new CodeMsg(500215, "密码错误"); //商品模块 5003XX //订单模块 5004XX public static CodeMsg ORDER_NOT_EXIST = new CodeMsg(500400, "订单不存在"); //秒杀模块 5005XX public static CodeMsg MIAO_SHA_OVER = new CodeMsg(500500, "商品已经秒杀完毕"); public static CodeMsg REPEATE_MIAOSHA = new CodeMsg(500501, "不能重复秒杀"); private CodeMsg( ) { } private CodeMsg( int code,String msg ) { this.code = code; this.msg = msg; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public CodeMsg fillArgs(Object... args) { int code = this.code; String message = String.format(this.msg, args); return new CodeMsg(code, message); } @Override public String toString() { return "CodeMsg [code=" + code + ", msg=" + msg + "]"; } }
md5前台后台两次加盐加密
一次:客户端密码加密,防止明文密码被劫持
二次:服务端shiro再加密一次
前台加密js代码:
function doLogin(){ g_showLoading(); var inputPass = $("#password").val(); var salt = g_passsword_salt; var str = ""+salt.charAt(0)+salt.charAt(2) + inputPass +salt.charAt(5) + salt.charAt(4); var password = md5(str); $.ajax({ url: "/login/do_login", type: "POST", data:{ mobile:$("#mobile").val(), password: password }, success:function(data){ layer.closeAll(); if(data.code == 0){ layer.msg("成功"); window.location.href="/goods/to_list"; }else{ layer.msg(data.msg); } }, error:function(){ layer.closeAll(); } }); }
后台MiaoshaUserService :
package com.javaxl.miaosha_02.service; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; import com.javaxl.miaosha_02.dao.MiaoshaUserDao; import com.javaxl.miaosha_02.domain.MiaoshaUser; import com.javaxl.miaosha_02.exception.GlobalException; import com.javaxl.miaosha_02.redis.MiaoshaUserKey; import com.javaxl.miaosha_02.redis.RedisService; import com.javaxl.miaosha_02.result.CodeMsg; import com.javaxl.miaosha_02.shiro.PasswordHelper; import com.javaxl.miaosha_02.util.MD5Util; import com.javaxl.miaosha_02.util.UUIDUtil; import com.javaxl.miaosha_02.vo.LoginVo; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class MiaoshaUserService { public static final String COOKI_NAME_TOKEN = "token"; @Autowired MiaoshaUserDao miaoshaUserDao; @Autowired RedisService redisService; public MiaoshaUser getById(long id) { //取缓存 MiaoshaUser user = redisService.get(MiaoshaUserKey.getById, ""+id, MiaoshaUser.class); if(user != null) { return user; } //取数据库 user = miaoshaUserDao.getById(id); if(user != null) { redisService.set(MiaoshaUserKey.getById, ""+id, user); } return user; } // http://blog.csdn.net/tTU1EvLDeLFq5btqiK/article/details/78693323 public boolean updatePassword(String token, long id, String formPass) { //取user MiaoshaUser user = getById(id); if(user == null) { throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST); } //更新数据库 MiaoshaUser toBeUpdate = new MiaoshaUser(); toBeUpdate.setId(id); toBeUpdate.setPassword(MD5Util.formPassToDBPass(formPass, user.getSalt())); miaoshaUserDao.update(toBeUpdate); //处理缓存 redisService.delete(MiaoshaUserKey.getById, ""+id); user.setPassword(toBeUpdate.getPassword()); redisService.set(MiaoshaUserKey.token, token, user); return true; } public MiaoshaUser getByToken(HttpServletResponse response, String token) { if(StringUtils.isEmpty(token)) { return null; } MiaoshaUser user = redisService.get(MiaoshaUserKey.token, token, MiaoshaUser.class); //延长有效期 if(user != null) { addCookie(response, token, user); } return user; } public String login(HttpServletResponse response, LoginVo loginVo) { if(loginVo == null) { throw new GlobalException(CodeMsg.SERVER_ERROR); } String mobile = loginVo.getMobile(); String formPass = loginVo.getPassword(); //判断手机号是否存在 MiaoshaUser user = getById(Long.parseLong(mobile)); if(user == null) { throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST); } //验证密码 String dbPass = user.getPassword(); String saltDB = user.getSalt(); if(!PasswordHelper.checkCredentials(formPass, saltDB, dbPass)) { throw new GlobalException(CodeMsg.PASSWORD_ERROR); } //生成cookie String token = UUIDUtil.uuid(); addCookie(response, token, user); return token; } private void addCookie(HttpServletResponse response, String token, MiaoshaUser user) { redisService.set(MiaoshaUserKey.token, token, user); Cookie cookie = new Cookie(COOKI_NAME_TOKEN, token); cookie.setMaxAge(MiaoshaUserKey.token.expireSeconds()); cookie.setPath("/"); response.addCookie(cookie); } }
全局异常处理
如果系统发生了异常,不做统一异常处理,前端会给用户展示一大片看不懂的文字。做统一异常处理后当异常发生后可以给用户一个温馨的提示,不至于使用户满头雾水,所以一方面是为了更好的用户体验 如果不统一全局异常,服务端和前端在遇到异常的时候处理起来杂乱无章非常费力。所以另一方面是为了制定规范提高工作效率
我们都知道使用 try-catch
可以捕捉异常,可以 throws
抛出异常。那么在 Spring Boot 中我们如何处理异常,如何处理得更加优雅呢
自定义异常类:
package com.javaxl.miaosha_02.exception; import com.javaxl.miaosha_02.result.CodeMsg; public class GlobalException extends RuntimeException{ private static final long serialVersionUID = 1L; private CodeMsg cm; public GlobalException(CodeMsg cm) { super(cm.toString()); this.cm = cm; } public CodeMsg getCm() { return cm; } }
全局异常处理类
package com.javaxl.miaosha_02.exception; import java.util.List; import javax.servlet.http.HttpServletRequest; import com.javaxl.miaosha_02.result.CodeMsg; import com.javaxl.miaosha_02.result.Result; import org.springframework.validation.BindException; import org.springframework.validation.ObjectError; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; @ControllerAdvice @ResponseBody public class GlobalExceptionHandler { @ExceptionHandler(value=Exception.class) public Result<String> exceptionHandler(HttpServletRequest request, Exception e){ e.printStackTrace(); if(e instanceof GlobalException) { GlobalException ex = (GlobalException)e; return Result.error(ex.getCm()); }else if(e instanceof BindException) { BindException ex = (BindException)e; List<ObjectError> errors = ex.getAllErrors(); ObjectError error = errors.get(0); String msg = error.getDefaultMessage(); return Result.error(CodeMsg.BIND_ERROR.fillArgs(msg)); }else { return Result.error(CodeMsg.SERVER_ERROR); } } }
通用的key生成策略
KeyPrefix :
package com.javaxl.miaosha_02.redis; public interface KeyPrefix { public int expireSeconds();//过期时间设置 public String getPrefix();//前缀 }
BasePrefix :
package com.javaxl.miaosha_02.redis; public abstract class BasePrefix implements KeyPrefix{ private int expireSeconds; private String prefix; public BasePrefix(String prefix) {//0代表永不过期 this(0, prefix); } public BasePrefix( int expireSeconds, String prefix) { this.expireSeconds = expireSeconds; this.prefix = prefix; } public int expireSeconds() {//默认0代表永不过期 return expireSeconds; } public String getPrefix() { String className = getClass().getSimpleName(); return className+":" + prefix; } }
以MiaoshaUserKey为例
package com.javaxl.miaosha_02.redis; public class MiaoshaUserKey extends BasePrefix{ public static final int TOKEN_EXPIRE = 3600*24 * 2; private MiaoshaUserKey(int expireSeconds, String prefix) { super(expireSeconds, prefix); } public static MiaoshaUserKey token = new MiaoshaUserKey(TOKEN_EXPIRE, "tk"); public static MiaoshaUserKey getById = new MiaoshaUserKey(0, "id"); }
通用的RedisService方法
高并发redis做缓存是很通用的手段,避免出现重复的redis操作方法
搞个通用的存值、取值、自增、自减...的方法
package com.javaxl.miaosha_02.redis; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.alibaba.fastjson.JSON; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; @Service public class RedisService { @Autowired JedisPool jedisPool; /** * 获取当个对象 * */ public <T> T get(KeyPrefix prefix, String key, Class<T> clazz) { Jedis jedis = null; try { jedis = jedisPool.getResource(); //生成真正的key String realKey = prefix.getPrefix() + key; String str = jedis.get(realKey); T t = stringToBean(str, clazz); return t; }finally { returnToPool(jedis); } } /** * 设置对象 * */ public <T> boolean set(KeyPrefix prefix, String key, T value) { Jedis jedis = null; try { jedis = jedisPool.getResource(); String str = beanToString(value); if(str == null || str.length() <= 0) { return false; } //生成真正的key String realKey = prefix.getPrefix() + key; int seconds = prefix.expireSeconds(); if(seconds <= 0) { jedis.set(realKey, str); }else { jedis.setex(realKey, seconds, str); } return true; }finally { returnToPool(jedis); } } /** * 判断key是否存在 * */ public <T> boolean exists(KeyPrefix prefix, String key) { Jedis jedis = null; try { jedis = jedisPool.getResource(); //生成真正的key String realKey = prefix.getPrefix() + key; return jedis.exists(realKey); }finally { returnToPool(jedis); } } /** * 删除 * */ public boolean delete(KeyPrefix prefix, String key) { Jedis jedis = null; try { jedis = jedisPool.getResource(); //生成真正的key String realKey = prefix.getPrefix() + key; long ret = jedis.del(key); return ret > 0; }finally { returnToPool(jedis); } } /** * 增加值 * */ public <T> Long incr(KeyPrefix prefix, String key) { Jedis jedis = null; try { jedis = jedisPool.getResource(); //生成真正的key String realKey = prefix.getPrefix() + key; return jedis.incr(realKey); }finally { returnToPool(jedis); } } /** * 减少值 * */ public <T> Long decr(KeyPrefix prefix, String key) { Jedis jedis = null; try { jedis = jedisPool.getResource(); //生成真正的key String realKey = prefix.getPrefix() + key; return jedis.decr(realKey); }finally { returnToPool(jedis); } } private <T> String beanToString(T value) { if(value == null) { return null; } Class<?> clazz = value.getClass(); if(clazz == int.class || clazz == Integer.class) { return ""+value; }else if(clazz == String.class) { return (String)value; }else if(clazz == long.class || clazz == Long.class) { return ""+value; }else { return JSON.toJSONString(value); } } @SuppressWarnings("unchecked") private <T> T stringToBean(String str, Class<T> clazz) { if(str == null || str.length() <= 0 || clazz == null) { return null; } if(clazz == int.class || clazz == Integer.class) { return (T)Integer.valueOf(str); }else if(clazz == String.class) { return (T)str; }else if(clazz == long.class || clazz == Long.class) { return (T)Long.valueOf(str); }else { return JSON.toJavaObject(JSON.parseObject(str), clazz); } } private void returnToPool(Jedis jedis) { if(jedis != null) { jedis.close(); } } }