SpringBoot构建电商基础秒杀项目知识点总结(第3章)


3-1 使用SpringMVC方式开发用户信息

1.springMVC中Model的设计思想

UserDO:dataobject仅仅只是与数据库表的一一映射,用mybatis generator自动生成,service层通过mapper从数据库中查询得到dataobject,必须转换为Model才能返回给controller.

UserModel:真正意义上springMVC中处理业务逻辑的核心的领域模型,(对于java的领域模型的概念来说,userMOdel中除了包含UserDO的所有字段,还应该包含密码字段)

UserVO:只包含前端需要展示的字段,

2.对象复制函数,用于不同层的model的转化

//仅当变量名和变量类型都相同时才能复制对象
BeanUtils.copyProperties(Object source, Object target)

3.需要在controller方法上加上@responseBody

@responseBody注解的作用是将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到response对象的body区,通常用来返回JSON数据或者是XML数据。
在使用@RequestMapping后,返回值通常解析为跳转路径。加上@responsebody后,返回结果直接写入HTTP response body中,不会被解析为跳转路径。

3-2 定义通用的返回对象–返回正确信息

1.封装正确的返回信息,使得前端能归一化处理

只要服务器能够受理请求,status都应该是200,如果业务逻辑中有错误,应该通过业务逻辑标志的方式实现.
该类使用status和data返回所有的Json序列化方式的固定对象供前端解析使用,摈弃掉了使用httpstatuscode和内嵌tomcat自带的error页的方式去处理.

public class CommonReturnType {

    //表明对应请求的返回处理结果“success”或“fail”
    private String status;

    //若status=success,则data内返回前端需要的json数据
    //若status=fail,则data内使用通用的错误码格式
    private Object data;

    //定义一个通用的创建方法
    public static CommonReturnType create(Object result) {
        return CommonReturnType.create(result, "success");
    }

    public static CommonReturnType create(Object result,String status) {
        CommonReturnType type = new CommonReturnType();
        type.setStatus(status);
        type.setData(result);
        return type;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public String getStatus() {

        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }
}

3-3 定义通用的返回对象–返回错误信息

1.封装错误的返回信息

public interface CommonError {
    public int getErrCode();

    public String getErrMsg();

    public CommonError setErrMsg(String errMsg);
}

EmBusinessError类的意义在于方便的统一管理所有错误码

public enum EmBusinessError implements CommonError {
	//前端会默认把errcode解析为string,将省略最左边的所有"0"
    //通用错误类型10001
    PARAMETER_VALIDATION_ERROR(10001, "参数不合法"),
    UNKNOWN_ERROR(10002, "未知错误"),

    //20000开头为用户信息相关错误定义
    USER_NOT_EXIST(20001, "用户不存在"),
    USER_LOOGIN_FAIL(20002, "用户手机号或密码不正确"),
    USER_NOT_LOGIN(20003, "用户还未登陆"),

    //30000开头为交易信息错误定义
    STOCK_NOT_ENOUGH(30001, "库存不足")
    ;

    private EmBusinessError(int errCode, String errMsg) {
        this.errCode = errCode;
        this.errMsg = errMsg;
    }

    private int errCode;
    private String errMsg;

    @Override
    public int getErrCode() {
        return this.errCode;
    }

    @Override
    public String getErrMsg() {
        return this.errMsg;
    }

	//该方法的意义在于,对于同一个错误码,可以通过定制化的方式改变气错误信息
    @Override
    public CommonError setErrMsg(String errMsg) {
        this.errMsg = errMsg;
        return this;
    }
}

设计模式:包装器业务异常实现

//包装器业务异常实现
public class BusinessException extends Exception implements CommonError {

    private CommonError commonError;

    //直接接受EmBusinessError的传参用于构造业务异常
    public BusinessException(CommonError commonError) {
        super();
        this.commonError = commonError;
    }

    //接收自定义errMsg的方式构造业务异常
    public BusinessException(CommonError commonError, String errMsg) {
        super();
        this.commonError = commonError;
        this.commonError.setErrMsg(errMsg);
    }

    @Override
    public int getErrCode() {
        return this.commonError.getErrCode();
    }

    @Override
    public String getErrMsg() {
        return this.commonError.getErrMsg();
    }

    @Override
    public CommonError setErrMsg(String errMsg) {
        this.commonError.setErrMsg(errMsg);
        return this;
    }
}

3-4 定义通用的返回对象–异常处理01&02

1.spring钩子的设计思想

上一节中我们定义的将在controller层使用的异常(继承了exception类),若不主动处理掉将会抛给tomcat层处理,结果就是向前端返回此异常,我们需要拦截此异常.
在BaseController中定义ExceptionHandler,让所有的Controller都继承此基类,于是若在后端到前端的最后一道关口–controller层中发生任何异常都能被及时捕获并处理.

    //定义exceptionHandler解决未被controller层吸收的exception
    //表示该方法处理所有的exception类
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.OK)
    //若不加@ResponseBody则返回的是页面路径,前端会寻找本地路径下的页面文件
    @ResponseBody
    public Object handlerException(HttpServletRequest request, Exception ex) {
        Map<String, Object> responseData = new HashMap<>();
        if (ex instanceof BusinessException) {
            BusinessException businessException = (BusinessException) ex;
            responseData.put("errCode", businessException.getErrCode());
            responseData.put("errMsg", businessException.getErrMsg());
        } else {
			//这里不直接将 EmBusinessError.UNKNOWN_ERROR传给CommonReturnType.create是因为 @ResponseBody所覆盖的Jackson默认的序列化方式会把Enum类型变成UNKNOWN_ERROR,不会有errCode和errMsg.
            responseData.put("errCode", EmBusinessError.UNKNOWN_ERROR.getErrCode());
            responseData.put("errMsg", EmBusinessError.UNKNOWN_ERROR.getErrMsg());
        }
        return CommonReturnType.create(responseData, "fail");
    }

3-6 用户模型管理–otp验证码获取

1.领域模型有自己完整的生命周期.

2/在企业级项目中,应用多为分布式,一般将用户的手机号和验证码存到redis中,redis本就是键值对存储,且当用户反复点击验证码时,将做覆盖,对应手机号的验证码永远只有最新的有效,并且redis有自己的时间,天生适合这种应用.

3.通过bean的方式注入进来的对象是单例模式

4.通过spring bean包装的HttpServletRequest本质是一个proxy,内部拥有thread local方式的map,能让用户在每个线程中处理自己的request,并且有thread local清除的机制.

3-8 用户模型管理–getotp页面实现

1.<meta charset="UTF-8">:保证页面中文不乱码
2.HTTP协议是以ASCII码传输,建立在TCP/IP协议之上的应用层规范.规范把HTTP请求分成三个部分:状态行,请求头,消息主题.协议没有规定消息主体的编码方式.需要开发者自己决定.对于POST请求,服务端通常根据请求头中的contentType字段来获知消息主体的编码方式再对其进行解析.
(如果 request header的content-type和方法中的consumer不匹配的话,那么该方法将无法处理。
如果 request header的accept和方法中的produces不匹配的话,那么该方法将无法处理。)

ajax中POST请求中常用的contentType:

(1) application/x-www-form-urlencoded
最常用的一种请求编码方式,在某些框架中也为默认值,支持GET/POST等方法,所有数据变成键值对的形式并且特殊字符需要转义成utf-8编号,如空格会变成 %20;
后端接收参数时,不用加@RequestBody,controller接收可以单个参数接收,如@RequestParam(“param”) String param;也可以用类接收User user。
且可以完美解决后端springmvc框架中,使用@RequestBody时,就不能再接收单个参数的问题:
缺点:当后端接收List参数时,使用application/x-www-form-urlencoded格式后端无法接收,需要转换为application/json,且后端使用@RequestBody接收.

(2) multipart/form-data
一般用来上传文件,使用表单上传文件时,必须指定表单的 enctype属性值为 multipart/form-data.请求体被分割成多部分,每部分使用 --boundary分割;

(3) application/json
后端接收参数时,需要加@RequestBody,犹豫json格式支持比键值对更复杂的结构化数据,特别适合RESTful接口.

3.@CrossOrigin:解决跨域问题.跨域是因为前端(本地)跟后端的域名不一致,所有这种请求叫跨域请求,加了那一行代码就是服务器放权给所有其他所有域名,让他们可以访问到服务器上面的资源.ajax启动的html对应的域是本地文件,但是请求的服务器是localhost域名,这种情况下请求能够正确达到服务端,服务端也能正确返回.ajax的请求回调认定,域不同不安全,会报错且走不到ajax的success block里.该注解使得Access-Control-Allow-Origin为"*",即所有的域.

3-10 用户模型管理–用户注册功能实现01

1.com.alibaba.druid.util.StringUtils.equals(String a, String b):内部已经实现了判空处理.
2.StringUtils.isEmpty(CharSequence cs):内部已经实现了判空和零长度.

    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.7</version>
    </dependency>
    public static boolean isEmpty(CharSequence cs) {
        return cs == null || cs.length() == 0;
    }

3.数据库字段设计中,尽量都设定非null,且定义默认值,在mybatis中则可以使用insertSelective和updateByPrimaryKeySelective,只插入和修改我们传入的字段,而对于未传入的字段直接使用数据库的默认值(java处理null很脆弱,null对前端展示无意义).
不能设定非null的情况:如果允许第三方注册且可以不用强绑定手机号,手机号若设定非null和默认值,则会有重复,不能加上唯一索引.不设定非null,则可以加上唯一索引(null不受唯一索引约束).
4.@Transactional:一般使用于service层的类以及类方法上,只对public方法有效.
只有来自外部的方法调用才会引起事务行为,类内部方法调用本类内部的其他方法并不会引起事务行为

3-11 用户模型管理–用户注册功能实现02

1.跨域授信

@CrossOrigin(allowCredentials = "true",allowedHeaders = "*"):

@CrossOrigin请求对于ajax请求不能做到session跨域共享,需要指定@CrossOrigin的范围,即allowCredentials = "true"和allowedHeaders = “*”,
DEFAULT_ALLOWED_HEADERS:允许跨域传输所有的header参数,将用于使用token放入header域做session共享的跨域请求.
DEFAULT_ALLOW_CREDENTIALS = ture:需配合前端设置授信(xhrFields:{withCredentials:true}:表示允许跨域授信请求使session变成跨域可授信)后才能使得跨域session共享.

2.MD5Encoder.encode(byte[] binaryData):java自带的MD5加密方式只支持16位字符串加密.

    public static String encode(byte[] binaryData) {
        if (binaryData.length != 16) {
            return null;
        } else {
            char[] buffer = new char[32];

            for(int i = 0; i < 16; ++i) {
                int low = binaryData[i] & 15;
                int high = (binaryData[i] & 240) >> 4;
                buffer[i * 2] = hexadecimal[high];
                buffer[i * 2 + 1] = hexadecimal[low];
            }

            return new String(buffer);
        }
    }

自定义任意位MD5加密

    //密码加密
    public String EncodeByMd5(String str) throws NoSuchAlgorithmException, UnsupportedEncodingException {
        //确定计算方法
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        BASE64Encoder base64en = new BASE64Encoder();
        //加密字符串
        String newstr = base64en.encode(md5.digest(str.getBytes("utf-8")));
        return newstr;
    }

3-13 优化校验规则

1.hibernate validator engine是支持了javax validator接口的实现,并可以通过注解的方式实现校验逻辑,是约定大于执行的过程.

    <!--校验-->
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>5.2.4.Final</version>
    </dependency>
public class ValidationResult {
    //校验结果是否有错
    private boolean hasErrors = false;

    //存放错误信息的map
    private Map<String, String> errorMsgMap = new HashMap<>();

    public boolean isHasErrors() {
        return hasErrors;
    }

    public void setHasErrors(boolean hasErrors) {
        this.hasErrors = hasErrors;
    }

    public Map<String, String> getErrorMsgMap() {
        return errorMsgMap;
    }

    public void setErrorMsgMap(Map<String, String> errorMsgMap) {
        this.errorMsgMap = errorMsgMap;
    }

    //实现通用的通过格式化字符串信息获取错误结果的msg方法
    public String getErrMsg() {
        return StringUtils.join(errorMsgMap.values().toArray(), ",");
    }
}
@Component
public class ValidatorImpl implements InitializingBean {

    private Validator validator;

    //实现校验方法并返回校验结果
    public ValidationResult validate(Object bean) {
        ValidationResult result = new ValidationResult();
        Set<ConstraintViolation<Object>> constraintViolationSet = validator.validate(bean);
        if (constraintViolationSet.size() > 0) {
            //有错误
            result.setHasErrors(true);
            constraintViolationSet.forEach(constraintViolation ->{
                String errMsg = constraintViolation.getMessage();
                String propertyName = constraintViolation.getPropertyPath().toString();
                result.getErrorMsgMap().put(propertyName, errMsg);
            });
        }
        return result;
    }

	//当spring bean初始化完成后悔回调此方法
    @Override
    public void afterPropertiesSet() throws Exception {
        //将hibernate validator通过工厂的初始化方式使其实例化
        this.validator = Validation.buildDefaultValidatorFactory().getValidator();
    }
}
@Data
public class UserModel {
    private Integer id;

    @NotBlank(message = "用户名不能为空")
    private String name;

    @NotNull(message = "性别不能不填写")
    private Byte gender;

    @NotNull(message = "年龄不能不填写")
    @Min(value = 0, message = "年龄必须大于0岁")
    @Max(value = 150, message = "年龄必须小于150岁")
    private Integer age;

    @NotBlank(message = "手机号不能为空")
    private String telphone;
    private String regisitMode;
    private Integer thirdPartyId;

    @NotBlank(message = "密码不能为空")
    private String encrptPassword;
    }
    ```
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值