两个切面的配置

前言

之前也学习过validate注解去校验一些参数的必要条件,这回工作中用的时候遇到了一些问题。这里都是使用spring boot框架去做的切面,省去了很多不需要的配置。这里去记录一下。

两个切面

validate注解

这里主要是去使用了hibernate中的注解而去做的切面,之前在博客中写到的整合了两个标准的注解的切面(注意区分下)。看一下切面的代码:

@Aspect
@Configuration
public class ValidateAspect {
    /**
     * BEAN对象校验器 配置快速失败模式
     */
    private final Validator BEAN_VALIDATOR = Validation.byProvider(HibernateValidator.class)
            .configure()
            //快速失败模式开启,当检测到有一项失败立即停止
            .failFast(true)
            .buildValidatorFactory().getValidator();

    /**
     * 方法参数对象校验器
     */
    private final ExecutableValidator METHOD_VALIDATOR = BEAN_VALIDATOR.forExecutables();

    /**
     * point配置
     */
    @Pointcut("execution(* com.zhanglijun.springbootdemo.web.controller..*.*(..))")
    public void pointcut() {
    }

    @Before("pointcut()")
    public void before(JoinPoint point) {

        //  获得切入目标对象
        Object target = point.getThis();
        // 获得切入方法参数
        Object [] args = point.getArgs();
        // 获得切入的方法
        Method method = ((MethodSignature)point.getSignature()).getMethod();

        // 校验以基本数据类型 为方法参数的
        checkWithResult(validMethodParams(target, method, args));

        // 校验以java bean对象
        for (Object bean : args) {
            if (null != bean) {
                checkWithResult(validBeanParams(bean));

            }
        }
    }

    /**
     * 校验bean对象中的参数
     * @param bean
     * @param <T>
     * @return
     */
    private <T> Set<ConstraintViolation<T>> validBeanParams(T bean) {
        return BEAN_VALIDATOR.validate(bean);
    }

    /**
     * 校验方法中的参数
     * @param obj
     * @param method
     * @param params
     * @param <T>
     * @return
     */
    private <T> Set<ConstraintViolation<T>> validMethodParams(T obj, Method method, Object [] params){
        return METHOD_VALIDATOR.validateParameters(obj, method, params);
    }

    /**
     * 校验参数校验结果
     * @param set
     */
    private void checkWithResult(Set<ConstraintViolation<Object>> set) {
        if (CollectionUtils.isEmpty(set)) {
            return;
        }
        Iterator<ConstraintViolation<Object>> methodIterator = set.iterator();
        if (methodIterator.hasNext()) {
            throw new IllegalArgumentException(methodIterator.next().getMessage());
        }
    }
}

这个可以去实现validate注解的切面配置。

访问接口信息注解

还有就是参考翟DD博客中的spring boot配置的一个记录访问接口信息的切面,这里在配置了之后遇到了两个问题,去记录一下。

切面配置
@Aspect
@Configuration
@Slf4j
public class WebRequestAspect {

    // TODO 可以加上sessionUser的获取

    @Pointcut("execution(* com.zhanglijun.springbootdemo.web.controller..*.*(..))")
    public void webLog(){}

    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) {
        // 获取request对象
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
            return;
        }
        HttpServletRequest request = attributes.getRequest();

        log.info("REQUEST_URL : {}, HTTP_METHOD : {}, ARGS : {}",
                request.getRequestURL().toString(),
                request.getMethod(),
                JSON.toJSONString(joinPoint.getArgs()));
    }

    @AfterReturning(returning = "ret", pointcut = "webLog()")
    public void doAfterReturning(Object ret) {
        // 打印返回内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
            // 容器启动时也去初始化了切面(因为是execution)这里去判断一下
            return;
        }
        HttpServletRequest request = attributes.getRequest();
        log.info("REQUEST_URL : {}, RESPONSE : {} ", request.getRequestURL(), JSON.toJSONString(ret));
    }
}
问题1 fastJson去打印入参

在doBefore方法中去做了对方法参数的打印,这里没有思索去用了fastJson去打印参数,但是当去访问一个参数中带有response对象的时,会发生报错。

接口:

//========================= 测试web log切面 =====================

@GetMapping(path = "/testAspect", produces = MediaType.APPLICATION_JSON_VALUE)
public void testAspect(HttpServletResponse response) {
    log.info("测试web log 切面 fastJson序列化response对象会报错");
}

报错:

这个错误在网上找了比较久,也跟进去源码看了,reponse的getOutputSteam在走到切面的这个输出的时候,已经被调用过了,会有一个标志位标志其被调用过,这里如果用fastJson去序列化这个response对象,这里会报错。这就提醒我们这里输出参数不能去使用fastJson去序列化。(有的mapping还是会去用到response对象)。修改方法就是可以把输出参数的部分改成Arrays.toString进行输出。

Arrays.toString(joinPoint.getArgs()));

这里要注意参数内部的toString方法的实现。

request对象获取的问题

这个切面中因为要记录访问的url,所以用到了HttpServletRequest对象,这里获取的方式是:RequestContextHolder.getRequestAttributes();这个实现的原理就是请求到来的时候去使用ThreadLocal放入request对象,所以在@AfterReturning中也可以去使用这个对象(因为这次请求还没有结束)。但是在项目初始化时,这个切面就会被加载一次,此时并没有请求所以ThreadLocal中也不会有这个request对象。

解决办法:我这里去对request对象做了判断,如果是null的话,那么方法直接返回。

这里也可以去写一下@AfterThrowing之后的处理,可以直接throw出去交给统一异常处理。这里注意切面的order属性和数据层面的事务的order大小顺序。order越小,执行的越靠前。

当你的切面order>数据层面的事务order时,执行顺序是:

transaction -> doBefore -> Exception -> @AfterThrowing -> rollback。注意这个时候会造成你的@AfterThrowing内容不生效,一起rollback了。

而当你的切面order < 数据层面事务order时,执行顺序是:

dobefore -> transaction -> exception -> rollback -> @AfterThrowing

所以要配置你的webLog切面的order小一些。在spring boot框架中可以通过@Order(level)在类上加上注解进行order控制。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值