|
@@ -0,0 +1,277 @@
|
|
|
+---
|
|
|
+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() + ")");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+```
|