欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SpringBoot 爬坑手记-2 AOP、表单验证与项目整理

程序员文章站 2022-05-31 23:52:11
...

    在第一篇博客中,我们编写了第一个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,并进行模拟测试:

SpringBoot 爬坑手记-2 AOP、表单验证与项目整理

SpringBoot 爬坑手记-2 AOP、表单验证与项目整理


我们会发现一个问题,两次返回的类型不一样,我们希望返回一个状态码,和一个描述。接下来我们将返回结果统一。

向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));
       
    }

再次运行:

SpringBoot 爬坑手记-2 AOP、表单验证与项目整理

SpringBoot 爬坑手记-2 AOP、表单验证与项目整理

使用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原理


SpringBoot 爬坑手记-2 AOP、表单验证与项目整理

控制台输出:

SpringBoot 爬坑手记-2 AOP、表单验证与项目整理

相关标签: Spring Boot java