---
title: aop
date: 2018-12-03 15:38:33
tags: [Spring Boot,AOP]
categories: [Java,Spring]
---
### spring aop 
通过自定义注解和aop实现参数校验

#### 配置
-  mvc 配置文件 添加 
```
<aop:aspectj-autoproxy proxy-target-class="true"/>
```
开启自动切面代理

- 添加自定义注解
```java
/**
 * @author : wangzhiyong
 * @date : 2018/11/12 17:41
 * description : 参数校验自定义注解
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
@Documented
@Inherited
public @interface ParamCheck {

    /**
     * 是否必填
     *
     * @return
     */
    public boolean require() default true;

    /**
     * 参数类型
     * {@link com.ys.tools.paramcheck.ParamType}
     *
     * @return
     */
    public ParamType type() default ParamType.STRING;

    /**
     * 长度
     *
     * @return
     */
    public int size() default Integer.MAX_VALUE;

    /**
     * 最小值
     *
     * @return
     */
    public double min() default Integer.MIN_VALUE;

    /**
     * 最大值
     *
     * @return
     */
    public double max() default Integer.MAX_VALUE;

    /**
     * 格式化
     *
     * @return
     */
    public String pattern() default "yyyy-MM-dd HH:mm:ss";


    /**
     * 是否是加密字段
     *
     * @return
     */
    public boolean cipher() default false;

}
``` 
- 添加指定类型
```java
public enum ParamType {
    /**
     * 基本参数类型
     */
    BYTE,SHORT,INT,LONG,FLOAT,DOUBLE,BOOLEAN,CHAR,
    /**
     * 拓展参数类型
     */
    STRING,DATE
}
```
- 添加处理逻辑
```java
@Aspect
@Component
public class ParamCheckAspect {

    private static final Logger logger = Logger.getLogger(ParamCheckAspect.class);

    //todo 暂时使用 方法扫描 以 O 结尾的方法 标识对外的接口 后期改为 @args
    //    @Pointcut("@args(com.ys.atds.annotation.ParamCheck)")
    // 定义切点 返回类型 或包名等
    @Pointcut("execution(* com.ys.*.api.impl..*.*O(..))")
    public void paramPointcut() {
    }

    // 指定切面类型 处理对应操作
    @Before("paramPointcut()")
    public void before(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        if (args == null || args.length == 0) {
            return;
        }
        // 方法的参数
        Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterTypes();
        for (int i = 0; i < parameterTypes.length; i++) {
            Class<?> parameterType = parameterTypes[i];
            // 若参数未注解ParamCheck, 忽略验证
            if (parameterType.getAnnotation(ParamCheck.class) == null) {
                continue;
            }
            // 传入的参数
            Object arg = args[i];
            if (arg == null) {
                throw new AtdsException("parameter should not be null or empty");
            }
            // 获取注解
            for (Field field : arg.getClass().getDeclaredFields()) {
                // 获取注解
                ParamCheck annotationsByType = field.getAnnotation(ParamCheck.class);
                // 只检查@ParamCheck注解字段
                if (annotationsByType == null) {
                    continue;
                }
                // 设置字段可访问并取值
                field.setAccessible(true);
                Object argValue = null;
                try {
                    argValue = field.get(arg);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                    logger.error("ParamCheckAspect: " + e.getMessage());
                }
                // 字段类型
                Class type = field.getType();
                // 字段为null或空string
                if (argValue == null || (String.class.equals(type) && "".equals(argValue))) {
                    // 若注解声明非空, 抛出异常, 否则忽略本字段
                    if (annotationsByType.require()) {
                        throw new AtdsException("parameter " + field.getName() + " should not be null or empty");
                    } else {
                        continue;
                    }
                }
                // 字段类型非数字 且非String, 忽略本字段, 不深入检查
                if (!isNumberType(type) && !String.class.equals(type)) {
                    continue;
                }
                try {
                    // 字段类型为 String 或 数字, 走各自验证
                    if (String.class.equals(type)) {
                        checkString(String.valueOf(argValue), annotationsByType);
                    } else {
                        checkNumber(argValue, annotationsByType);
                    }
                } catch (AtdsException e) {
                    throw new AtdsException(String.format(e.getMessage(), field.getName()));
                }
            }
        }
    }

    /**
     * 是否数字类型
     *
     * @param type
     * @return
     */
    private boolean isNumberType(Class type) {
        return isNumberType(type.getSimpleName());
    }

    /**
     * 是否数字类型
     *
     * @param typeName
     * @return
     */
    private boolean isNumberType(String typeName) {
        if ("integer".equalsIgnoreCase(typeName) || ParamType.INT.name().equalsIgnoreCase(typeName)) {
            return true;
        } else if ("long".equalsIgnoreCase(typeName) || ParamType.LONG.name().equalsIgnoreCase(typeName)) {
            return true;
        } else if ("double".equalsIgnoreCase(typeName) || ParamType.DOUBLE.name().equalsIgnoreCase(typeName)) {
            return true;
        } else if ("float".equalsIgnoreCase(typeName) || ParamType.FLOAT.name().equalsIgnoreCase(typeName)) {
            return true;
        } else if ("byte".equalsIgnoreCase(typeName) || ParamType.BYTE.name().equalsIgnoreCase(typeName)) {
            return true;
        } else if ("short".equalsIgnoreCase(typeName) || ParamType.SHORT.name().equalsIgnoreCase(typeName)) {
            return true;
        } else if ("character".equalsIgnoreCase(typeName) || ParamType.CHAR.name().equalsIgnoreCase(typeName)) {
            return true;
        }
        return false;
    }

    /**
     * 检查String类型的参数
     * 包括 size/根据 type 检查 min/max/pattern
     *
     * @param value
     * @param paramCheck
     */
    private void checkString(String value, ParamCheck paramCheck) throws AtdsException {
        // 检查长度
        if (paramCheck.size() < value.length()) {
            throw new AtdsException("parameter %s is too long. (" + value.length() + " of " + paramCheck.size() + ")");
        }
        // 日期时
        if (ParamType.DATE.equals(paramCheck.type())) {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern(paramCheck.pattern());
            try {
                formatter.parse(value);
            } catch (Exception e) {
                throw new AtdsException("parameter %s needs correct pattern. (" + paramCheck.pattern() + ")");
            }
        } else if (ParamType.BOOLEAN.equals(paramCheck.type())) {
            if (!"true".equals(value) && !"false".equals(value)) {
                throw new AtdsException("parameter %s is not a " + paramCheck.type());
            }
        } else if (isNumberType(paramCheck.type().name())) {
            checkNumber(value, paramCheck);
        }

    }

    /**
     * 检查数字类型(BYTE/SHORT/INT/LONG/FLOAT/DOUBLE)以及字符类型的参数
     * 包括 min/max
     *
     * @param value
     * @param paramCheck
     */
    private void checkNumber(Object value, ParamCheck paramCheck) {
        BigDecimal numberValue;
        try {
            String stringValue = value.toString();
            if (ParamType.CHAR.equals(paramCheck.type())) {
                if (stringValue.length() != 1) {
                    throw new NumberFormatException();
                }
                numberValue = new BigDecimal(stringValue.toCharArray()[0]);
            } else {
                numberValue = new BigDecimal(stringValue);
            }
        } catch (NumberFormatException e) {
            throw new AtdsException("parameter %s is not a " + paramCheck.type());
        }
        // 检查最大值
        BigDecimal min = new BigDecimal(paramCheck.min());
        if (numberValue.compareTo(min) < 0) {
            throw new AtdsException("parameter %s is out of range. (min: " + paramCheck.min() + ")");
        }
        // 检查最小值
        BigDecimal max = new BigDecimal(paramCheck.max());
        if (numberValue.compareTo(max) > 0) {
            throw new AtdsException("parameter %s is out of range. (max: " + paramCheck.max() + ")");
        }
    }

}
```