UserDO:dataobject仅仅只是与数据库表的一一映射,用mybatis generator自动生成,service层通过mapper从数据库中查询得到dataobject,必须转换为Model才能返回给controller.
UserModel:真正意义上springMVC中处理业务逻辑的核心的领域模型,(对于java的领域模型的概念来说,userMOdel中除了包含UserDO的所有字段,还应该包含密码字段)
UserVO:只包含前端需要展示的字段,
//仅当变量名和变量类型都相同时才能复制对象
BeanUtils.copyProperties(Object source, Object target)
@responseBody注解的作用是将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到response对象的body区,通常用来返回JSON数据或者是XML数据。
在使用@RequestMapping后,返回值通常解析为跳转路径。加上@responsebody后,返回结果直接写入HTTP response body中,不会被解析为跳转路径。
只要服务器能够受理请求,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;
}
}
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;
}
}
上一节中我们定义的将在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");
}
1.领域模型有自己完整的生命周期.
2/在企业级项目中,应用多为分布式,一般将用户的手机号和验证码存到redis中,redis本就是键值对存储,且当用户反复点击验证码时,将做覆盖,对应手机号的验证码永远只有最新的有效,并且redis有自己的时间,天生适合这种应用.
3.通过bean的方式注入进来的对象是单例模式
4.通过spring bean包装的HttpServletRequest本质是一个proxy,内部拥有thread local方式的map,能让用户在每个线程中处理自己的request,并且有thread local清除的机制.
1.:保证页面中文不乱码
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为"*",即所有的域.
1.com.alibaba.druid.util.StringUtils.equals(String a, String b):内部已经实现了判空处理.
2.StringUtils.isEmpty(CharSequence cs):内部已经实现了判空和零长度.
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-lang3artifactId>
<version>3.7version>
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方法有效.
只有来自外部的方法调用才会引起事务行为,类内部方法调用本类内部的其他方法并不会引起事务行为
@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共享.
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;
}
<dependency>
<groupId>org.hibernategroupId>
<artifactId>hibernate-validatorartifactId>
<version>5.2.4.Finalversion>
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;
}
```