Spring aop 一些问题

前言

我们通常可以用aop做一些公共的问题,比如:权限管理,事务委托,验证,日志管理。

通知

环绕通知和前置通知,后置通知

  • 前置和后置通知是在方法的调用前后执行通知,目标方法一定会执行的,然而环绕通知是可以决定方法是否可以执行的。

  • 环绕通知可以控制返回对象,即你可以返回一个与目标对象完全不同的返回值,虽然这很危险,但是你却可以办到。而后置方法是无法办到的,因为他是在目标方法返回值后调用

参数验证aop Demo

@Aspect
@Component
public class HibernateValidatorAspect implements Ordered {
    private static final Logger LOGGER = LoggerFactory.getLogger(HibernateValidatorAspect.class);
    private final int order;
    private final RestStatus throwIfInvalidModel;

    public HibernateValidatorAspect() {
        this(Byte.MAX_VALUE);
    }

    public HibernateValidatorAspect(int order) {
        this(order, DefaultInvalidModelStatus.INVALID_MODEL_STATUS);
    }

    public HibernateValidatorAspect(int order, RestStatus throwIfInvalidModel) {
        this.order = order;
        this.throwIfInvalidModel = throwIfInvalidModel;
    }

    @Around(value = "within(com.spring..*)&&(@annotation(org.springframework.web.bind.annotation.PostMapping)|| @annotation(org.springframework.web.bind.annotation.RequestMapping))")
    public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        for (Object arg : joinPoint.getArgs()) {
            if (arg instanceof BindingResult) {
                throwIfInvalidModel((BindingResult) arg, throwIfInvalidModel);
            }
        }
        return joinPoint.proceed();
    }



    /**
     * 校验实体合法性, 自动向Map封装错误信息.
     *
     * @param result Spring MVC中与@Valid成对出现的BindingResult, 用于绑定错误信息
     * @throws IllegalValidateException 实体校验失败异常
     * @see org.springframework.web.bind.annotation.ControllerAdvice
     */
    public static void throwIfInvalidModel(BindingResult result, RestStatus errorStatus) {
        Preconditions.checkNotNull(result);
        // 默认为true, 检测到错误时赋值为false
        boolean isValid = true;
        final HashMap<Object, Object> errorMap = Maps.newHashMap();
        if (result.getErrorCount() > 0) {
            isValid = false;
            String errorFieldName;
            for (FieldError fieldError : result.getFieldErrors()) {
                errorFieldName = acquireFieldName(result, fieldError);
                final String errorMessage = fieldError.getDefaultMessage();
                LOGGER.debug("request id: {}, error field: {}, error msg: {}",
                        ServletContextHolder.fetchRequestId(), errorFieldName, errorMessage);
                errorMap.put(errorFieldName, errorMessage);
            }
        }
        if (!isValid) {
            final ErrorEntity entity = new ErrorEntity(errorStatus, errorMap);
            // 以entity中的code为key存入Request中
            final String errorCode = String.valueOf(errorStatus.code());
            bindStatusCodesInRequestScope(errorCode, entity);
            throw new IllegalValidateException(errorCode);
        }
    }

    /**
     * 获取错误的字段名, 如果被{@link JsonProperty}修饰则优先选择
     *
     * @see JsonProperty
     */
    private static String acquireFieldName(BindingResult result, FieldError fieldError) {
        Preconditions.checkNotNull(result);
        Preconditions.checkNotNull(fieldError);
        // 获取错误字段名
        String errorFieldName = fieldError.getField();
        // 获取校验非法的类
        Class<?> clazz = result.getTarget().getClass();
        final Field field;
        try {
            // 获取其字段名
            field = clazz.getDeclaredField(fieldError.getField());
            final JsonProperty annotation = field.getAnnotation(JsonProperty.class);
            // 若JsonProperty里value()不为null则覆盖该值
            if (annotation != null) {
                errorFieldName = annotation.value();
            }
        } catch (NoSuchFieldException e) {
            LOGGER.error("request id: {}, 反射字段名时抛出异常: {}", ServletContextHolder.fetchRequestId(), e.getMessage());
        }
        return errorFieldName;
    }

    private static void bindStatusCodesInRequestScope(String key, ErrorEntity entity) {
        Preconditions.checkNotNull(entity);
        Preconditions.checkNotNull(key);
        final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes != null) {
            ((ServletRequestAttributes) requestAttributes).getRequest().setAttribute(key, entity);
        }
    }

    @Override
    public int getOrder() {
        return order;
    }

    private enum DefaultInvalidModelStatus implements RestStatus {
        INVALID_MODEL_STATUS(40001, "invalid request model");

        DefaultInvalidModelStatus(int code, String messge) {
            this.code = code;
            this.message = messge;
        }

        private final int code;

        private final String message;

        @Override
        public int code() {
            return code;
        }

        @Override
        public String message() {
            return message;
        }
    }
}

请注意上面的切面类实现了 Ordered 接口,这样我们就可以把切面的优先级设定为高于事务通知(我们每次重试的时候都想要在一个新的事务中进行)。 maxRetries 和 order 属性都在Spring中配置。

如何配置 @around @before @after 里面的切面点可以参考
spring aop

within 和 annotation

表示切入点在 包com.spring 下的所有方法

within(com.spring..*)

表示切入点在 有注解service 所有的类

within(@org.springframework.stereotype.Service *)

等于

@within(org.springframework.stereotype.Service)

表示有注解PostMapping的所有方法

@annotation(org.springframework.web.bind.annotation.PostMapping)

表示切入点在包com.spring下所有的方法,并且有PostMapping或RequestMapping注解的

 @Around(value = "within(com.spring..*)&&(@annotation(org.springframework.web.bind.annotation.PostMapping)|| @annotation(org.springframework.web.bind.annotation.RequestMapping))")
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值