目录:
lambda表达式
表单校验
单元测试的书写
抽象复用性强的代码
配置自动装载
AOP进行页面登录验证
数据类型序列化
JSON与对象的转换
控制器异常处理
lambda表达式
样例:
List<CartDTO> cartDTOList = orderDTO.getOrderDetailList()
.stream().map(e -> new CartDTO(e.getProductId(), e.getProductQuantity()))
.collect(Collectors.toList());
使用场景
> 需要对两个不同的数据类(List)进行转换。
> 比如这个例子,需要将OrderDetailList 转成 CartDTOList
> 更多的用法,可以参考这篇博客:https://www.cnblogs.com/aoeiuv/p/5911692.html
表单校验
样例:
@PostMapping("/create")
public ResultVO<Map<String, String>> create(@Valid OrderForm orderForm,
BindingResult bindingResult)
{}
package com.product.sell.sell.form;
import lombok.Data;
import org.hibernate.validator.constraints.NotEmpty;
@Data
public class OrderForm {
@NotEmpty(message="姓名必填")
private String name;
@NotEmpty(message = "手机号必填")
private String phone;
@NotEmpty(message = "地址必填")
private String address;
@NotEmpty(message = "openid必填")
private String openid;
@NotEmpty(message = "购物车不能为空")
private String items;
}
一般需要对参数进行验证的,都是保存的post接口
由于数据量比较大,直接定义一个对象来对传递的表单进行存储。
然后在FORM类里,通过注解的形式进行校验。
控制器层通过
if (bindingResult.hasErrors()) {
log.error("【创建订单】参数不正确:orderForm={}", orderForm);
throw new SellException(ResultEnum.PARAM_ERR.getCode(),
bindingResult.getFieldError().getDefaultMessage());
}
来获取到校验的结果
单元测试的书写
单元测试中需要加上这两个注解。
@RunWith(SpringRunner.class)
@SpringBootTest
public class OrderServiceImplTest {}
具体的单元测试:
@Test
public void findOne() {
String orderId = "154494950704674973";
OrderDTO result = orderService.findOne(orderId);
log.info("查询单个订单", result);
Assert.assertNotNull(result);
}
抽象复用性强的代码逻辑
场景:需要通过一个status值,获取到对应的中文解析。(1-》激活 0-》冻结)
这种一般用在后台,需要对数字进行解析。常规做法就是写 if 判断一下。
但是会存在非常多的重复性代码。
比较优秀的做法是,通过两个参数,直接获取到相对应的中文描述。
两个参数分别是 Integer status, Class<T> enumClass
public class EnumUtil {
// T 这里有可能是任何一种类型的枚举类
// T extends codeenum 这个接口 保证了他一定实现了接口里的方法
// 这就是写 CodeEnum,并让你的枚举类实现它的原因所在
public static <T extends CodeEnum> T getByCode(Integer code, Class<T> enumClass)
{
for (T each : enumClass.getEnumConstants()) {
if (code.equals(each.getCode())) {
return each;
}
}
return null;
}
}
这里Class 为啥要加一个 T呢?具体的解释,可以参考这篇博客: https://www.cnblogs.com/zhima-hu/p/7352555.html
通俗的讲,就是指任何一个类。
但是代码里用到了一个方法:
each.getCode()
如何保证传入的类一定有这个方法呢?
<T extends CodeEnum>
public interface CodeEnum {
// 因为本次项目中code全部都是 Integer
// 如果项目比较大,可以定义为 泛型
Integer getCode();
}
这样就保证了作为参数传入的类,必定实现了 getCode 这个方法,因此可以放心的使用。
然后我们在具体的数据类中,写一个方法:
@Data
//@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
// 上面这个废弃了。
// 如果结果中有null,则不去掉这个属性
// 还可以在yml里做全局配置.jackson ---
//@JsonInclude(JsonInclude.Include.NON_NULL)
public class OrderDTO {
...
@JsonIgnore
public OrderStatus getOrderStatusEnum()
{
return EnumUtil.getByCode(orderStatus, OrderStatus.class);
}
@JsonIgnore
public PayStatus getPayStatusEnum()
{
return EnumUtil.getByCode(payStatus, PayStatus.class);
}
}
lombok插件
引入依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
即可使用注解的方式,免去getter setter的代码噩梦
记得要需要给 Idea 安装一个插件
配置自动装载
这次涉及到微信的开发,里面有些Jar包需要进行初始化配置(appID,secretID等)
这些配置内容,不写在控制器层,也不写在service层。而是直接将其装配成 bean。
@Component
public class WechatPayConfig {
@Autowired
private WechatAccountPayConfig wechatAccountPayConfig;
@Bean
public BestPayServiceImpl bestPayService()
{
BestPayServiceImpl bestPayService = new BestPayServiceImpl();
bestPayService.setWxPayH5Config(wxPayH5Config());
return bestPayService;
}
@Bean
public WxPayH5Config wxPayH5Config()
{
WxPayH5Config wxPayH5Config = new WxPayH5Config();
wxPayH5Config.setAppId(wechatAccountPayConfig.getMpAppId());
wxPayH5Config.setAppSecret(wechatAccountPayConfig.getMpAppSecret());
wxPayH5Config.setKeyPath(wechatAccountPayConfig.getKeyPath());
wxPayH5Config.setMchId(wechatAccountPayConfig.getMchId());
wxPayH5Config.setMchKey(wechatAccountPayConfig.getMchKey());
wxPayH5Config.setNotifyUrl(wechatAccountPayConfig.getNotifyUrl());
return wxPayH5Config;
}
}
@Data
@Component
@ConfigurationProperties(prefix = "wechatpay")
public class WechatAccountPayConfig {
private String mpAppId;
private String mpAppSecret;
// 微信商户的一些配置信息
private String mchId;
private String mchKey;
private String keyPath;
private String notifyUrl;
}
AOP进行页面登录验证
跟Laravel的middleware很类似。
通过一个pointcut,来定义需要进行切面验证的接口进行定义
通过几个方法(doBefore 和 doAfter)来定义进入这个方法之前(之后)需要做的逻辑
这边逻辑还有点不对。
登录这块儿的代码,需要重新写下。
public class HttpAspact {
private final static Logger logger = LoggerFactory.getLogger(HttpAspact.class);
@Pointcut("execution(public * com.product.sell.sell.controller.BuyerOrderController.list(..))")
public void log(){}
@Before("log()")
public void doBefore(JoinPoint joinPoint)
{
ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// url
logger.info("url={}", request.getRequestURL());
// method
logger.info("method={}", request.getMethod());
// IP
logger.info("ip={}", request.getRemoteAddr());
// class 方法
logger.info("class method = {}", joinPoint.getSignature().getDeclaringTypeName() +
"." +
joinPoint.getSignature().getName());
// 参数
logger.info("args={}", joinPoint.getArgs());
}
@After("log()")
public void doAfter()
{
}
数据类型序列化 6-12
一般像ctime,mtime这种类型的字段,DAO里定义成 Date 类型的。
但是可能前端需要的格式不是这个类型,而是一个String类型的时间戳,如何进行转换呢?
@JsonSerialize(using = DateToLongSerializer.class)
private Date updateTime;
这里用到了 JsonSerialize 注解。
public class DateToLongSerializer extends JsonSerializer<Date> {
@Override
public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeNumber(date.getTime() / 1000);
}
}
JSON与对象的转换 6-11
代码里定义了一个 List 的值,但是前端表单传过来的是一个json字符串。
需要对这个字符串进行转换
public static OrderDTO convert(OrderForm orderForm)
{
Gson gson = new Gson();
OrderDTO orderDTO = new OrderDTO();
List<OrderDetail> orderDetailList = new ArrayList<>();
try {
orderDetailList = gson.fromJson(orderForm.getItems(),
new TypeToken<List<OrderDetail>>(){}.getType());
} catch (Exception e) {
log.error("【对象转化出错】,string={}", orderForm.getItems());
throw new SellException(ResultEnum.PARAM_ERR);
}
orderDTO.setOrderDetailList(orderDetailList);
return orderDTO;
}
控制器异常处理
在程序抛出异常时,接口不能返回500错误,而是要返回异常信息。格式不能乱
@ControllerAdvice
public class ExceptionHandle {
@ExceptionHandler(value = SellException.class)
@ResponseBody
public ResultVO handle(Exception e)
{
if (e instanceof SellException) {
SellException sellException = (SellException) e;
return ResultVOUtil.error(sellException.getCode(), sellException.getMessage());
}
return ResultVOUtil.error(-1, e.getMessage());
}
}