SpringBoot 爬坑手记-2 AOP、表单验证与项目整理
在第一篇博客中,我们编写了第一个SpringBoot程序,实现了与mysql数据库的简单交互。在之前的项目中,我们将所有的.class文件几乎都放在一个包内,包括controller,service,properties,domain等,在本文中,我们将将这几个模块放在不同的包中,并添加aspect(使用aop的编程思想进行日志记录等操作)、enums(枚举类型、记录错误代码与类型)。
表单验证
新建domain包,将Girl.class移入其中。在该类中,我们定义了Girl这个实体,现在我们向age段添加验证条件。假设我们的数据库不允许18岁以下女孩进入。
在age前加入@Min注解
@Min(value = 18,message = "未成年禁止入内")
private Integer age
加入用户传入了一个age = 16的数据,我们如何得到返回信息呢?
接下来,新建controller包,并将GirlController移到该包内。
编写插入一个女孩的方法:
@PostMapping(value = "/girls")
public Object girlInsert(@Valid Girl girl, BindingResult bindingResult){
if (bindingResult.hasErrors()){
return bindingResult.getFieldError().getDefaultMessage();
}
girl.setCupSize(girl.getCupSize());
girl.setAge(girl.getAge());
return girlReponsitory.save(girl);
}
打开测试工具postman,并进行模拟测试:
我们会发现一个问题,两次返回的类型不一样,我们希望返回一个状态码,和一个描述。接下来我们将返回结果统一。
向domain包中添加Result类:
public class Result<T>{
private int code;
private String;
private T data;
//getters and setters
}
然后将之前的girlInsert类改写:
@Autowired
private GirlReponsitory girlReponsitory;
@PostMapping(value = "/girls")
public Result<Girl> girlInsert(@Valid Girl girl,BindingResult bindingResult){
if(bindingResult.hasErrors())
{
Result result = new Result();
result.setData(null);
result.setCode(1);
result.setMessage(bindingResult.getFieldError().getDefaultMessage());
return result
}
girl.setCupSize(girl.getCupSize());
girl.setAge(girl.getAge);
Result result = new Result();
result.setdata(girlReponsitory.save(girl));
result.setcode(0);
result.setMessage(
return result;
}
我们发现代码中有重复的部分,现在优化代码,新建utils包,并创建类ResultUtil:
public class ResultUtil {
public static Result success(Object object){
Result result = new Result();
result.setCode(0);
result.setData(object);
result.setMsg("成功");
return result;
}
public static Result success(){
return success(null);
}
public static Result error(Integer code,String msg){
Result result = new Result();
result.setCode(code);
result.setData(null);
result.setMsg(msg);
return result;
}
}
接下来可将上面的代码简化为:
@PostMapping(value = "/girls")
public Object girlInsert(@Valid Girl girl, BindingResult bindingResult){
if (bindingResult.hasErrors()){
return ResultUtil.error(1,bindingResult.getFieldError().getDefaultMessage());
}
girl.setCupSize(girl.getCupSize());
girl.setAge(girl.getAge());
return ResultUtil.success(girlReponsitory.save(girl));
}
再次运行:
使用AOP处理请求
为什么需要Aop?
我们有这样的业务需求:需要在每次请求时将操作记录进日志。我们可能想到的方法是:在Controller中,将每个方法前都加入进行日志操作的语句。这样做会使我们的代码耦合性提高,可以想到,当需要修改日志操作时,要一个个的改函数定义,这让我们希望有更好的解决方法。
这是AOP出现了,AOP将特定业务逻辑的关注点和一些通用逻辑关注点分离开。这些通用化功能的代码实现,就是我们所说的切面。
AOP是一种编程思想,类比OOP(面向对象编程)POP(面向过程编程)。
AOP,在SpringBoot中,主要解决系统层面的问题,比如:日志,事物,权限等待。有以下几个概念:
Aspect:切面:通常是一个类,在类里面定义可以定义切入点。
PointCut:切入点:定义了在何处织入,在Spring框架中,使用@PointCut定义
Advice:通知:通知就是定义类里的方法如何以什么样的方式来织入。在Spring中,可以用@Before @After @AfterReturning等来实现
Target:目标对象:要被织入 的对象。
新建包aspect,新建类:HttpAspect.class .这是上文中所说的Aspect(切面)。
@Aspect
@Component
public class HttpAspect {
private final static Logger logger = LoggerFactory.getLogger(com.zhe.demo.aspect.HttpAspect.class);
@Pointcut("execution(public * com.zhe.demo.controller.*.*(..))")
public void log(){}
//记录http请求
@Before("log()")
public void doBefore(JoinPoint joinPoint){
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();
//url
logger.info("url={}",request.getRequestURL());
//method
logger.info("method={}",request.getMethod());
//ip
logger.info("method={}",request.getRemoteAddr());
//类方法
logger.info("class_method:{}",joinPoint.getSignature().getDeclaringTypeName()+"."+joinPoint.getSignature().getName());
//参数
logger.info("args={}",joinPoint.getArgs());
}
@After("log()")
public void doAfter(){
logger.info("finish");
}
@AfterReturning(returning = "object",pointcut = "log()")
public void doAfterReturning(Object object){
logger.info("response = {}",object.toString());
}
}
在上面的代码中,我们使用
@Pointcut("execution(public * com.zhe.demo.controller.*.*(..))")
public void log(){}
定义了切入点,即在com.zhe.dem.controller包中的所有类的所有函数传入任意参数时使用(参数传递使用了正则表达式),具体语法可参考https://www.tianmaying.com/tutorial/spring-aop-point-advice
使用
@Before("log()") @After("log()")
定义advice,即用什么样的方式来织入 。
更为详细的解释可参考:Spring AOP原理
控制台输出: