Spring——自定义注解

1.参数注解

  • 注解
/**
 * 字段参数注解
 *
 * @author A.keung
 * 2019/8/30 0030
 * @Constraint 通过使用validatedBy指定与注解关联的验证器
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = ParamsCheckConstraintValidator.class)
public @interface ParamsCheck {
    //参数
    String[] paramValues();

    //提示
    String message() default "params error";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}
  • 验证器
/**
 * 字段参数注解验证器
 *
 * @author A.keung
 * 2019/8/30 0030
 */
public class ParamsCheckConstraintValidator implements ConstraintValidator<ParamsCheck, Object> {
    //注解参数
    private List<String> paramValues;

    @Override
    public void initialize(ParamsCheck paramsCheck) {
        //初始化时获取注解上的值
        paramValues = Arrays.asList(paramsCheck.paramValues());
    }

    @Override
    public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
        return paramValues.contains(o);
    }
}
  • 测试
  @RequestMapping(value = "testParamsCheck",method = RequestMethod.POST)
    public String testParamsCheck(@Validated @RequestBody User user) {
        log.info(JSON.toJSONString(user));
        return "OK";
    }
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@TableName("t_user")
@NoArgsConstructor
//@AllArgsConstructor
public class User extends BaseEntity{
    /**
     * 密码
     */
    @TableField("password")
    private String password;
    /**
     * 手机
     */
    @TableField("phone_number")
    private String phoneNumber;
    /**
     * 邮箱
     */
    @TableField("email")
    private String email;
    /**
     * 昵称
     */
    @TableField("nickname")
    private String nickname;
    /**
     * 权限
     */
    @ParamsCheck(paramValues = {"ADMIN","USER"})
    @TableField("role")
    private String role;
    /**
     * qqOpenId
     */
    @TableField("qq_open_id")
    private String qqOpenId;
    /**
     * 头像地址
     */
    @TableField("figure_url")
    private String figureUrl;
    /**
     * 是否启用
     */
    @TableField("enable")
    private Boolean enable;

    private User(Builder builder) {
        setId(builder.id);
        setCreatedDatetime(builder.createdDatetime);
        setModifiedDatetime(builder.modifiedDatetime);
        setPassword(builder.password);
        setPhoneNumber(builder.phoneNumber);
        setEmail(builder.email);
        setNickname(builder.nickname);
        setRole(builder.role);
        setQqOpenId(builder.qqOpenId);
        setFigureUrl(builder.figureUrl);
        setEnable(builder.enable);
    }


    public static final class Builder {
        private Long id;
        private Date createdDatetime;
        private Date modifiedDatetime;
        private String password;
        private String phoneNumber;
        private String email;
        private String nickname;
        private String role;
        private String qqOpenId;
        private String figureUrl;
        private Boolean enable;

        public Builder() {
        }

        public Builder id(Long val) {
            id = val;
            return this;
        }

        public Builder createdDatetime(Date val) {
            createdDatetime = val;
            return this;
        }

        public Builder modifiedDatetime(Date val) {
            modifiedDatetime = val;
            return this;
        }

        public Builder password(String val) {
            password = val;
            return this;
        }

        public Builder phoneNumber(String val) {
            phoneNumber = val;
            return this;
        }

        public Builder email(String val) {
            email = val;
            return this;
        }

        public Builder nickname(String val) {
            nickname = val;
            return this;
        }

        public Builder role(String val) {
            role = val;
            return this;
        }

        public Builder qqOpenId(String val) {
            qqOpenId = val;
            return this;
        }

        public Builder figureUrl(String val) {
            figureUrl = val;
            return this;
        }

        public Builder enable(Boolean val) {
            enable = val;
            return this;
        }

        public User build() {
            return new User(this);
        }
    }
}

2.缓存注解

  • 注解
/**
 * 部门缓存注解
 *
 * @author A.keung
 * 2019/8/30 0030
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DeptCache {
    //#user.name或jack
    String keyExpr();
}
  • 切面
/**
 * 部门缓存切面
 *
 * @author A.keung
 * 2019/8/30 0030
 */
@Component
@Aspect
@Slf4j
public class DeptCacheAspect {

    /**
     * 对含有注解的方法进行处理
     *
     * @param joinPoint
     * @return 返回值
     */
    @Around("@annotation(com.ak.common.DeptCache)")
    public Object process(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
        Object result = null;
        // 获取切入的方法对象
        // 代理对象的,没有包含注解
        Method m = ((MethodSignature) joinPoint.getSignature()).getMethod();
        // 目标对象反射获取的method对象才包含注解
        Method methodWithAnnotations = joinPoint.getTarget().getClass().getDeclaredMethod(joinPoint.getSignature().getName(), m.getParameterTypes());
        // 根据目标方法对象获取注解对象
        DeptCache deptCache = methodWithAnnotations.getDeclaredAnnotation(DeptCache.class);
        // 解析key
        String keyExpr = deptCache.keyExpr();
        Object[] argValues = joinPoint.getArgs();
        String key = parseKey(methodWithAnnotations, argValues, keyExpr);
        //根据key查询缓存
        String cache = findCache(key);
        if (StringUtils.isNotBlank(cache)) {
            return cache;
        }
        //执行目标方法
        try {
            result = joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return result;
    }

    /**
     * 解析key(只支持String)
     *
     * @param method
     * @param argValues 方法参数值
     * @param keyExpr   注解值或表达式(含#)
     * @return key
     * @throws IllegalAccessException
     */
    private String parseKey(Method method, Object[] argValues, String keyExpr) {
        if (keyExpr.startsWith("#")) {
            String[] exprParamNames = keyExpr.substring(keyExpr.indexOf('#') + 1).split("\\.");
            // 获取方法参数名列表
            LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
            String[] paramNames = discoverer.getParameterNames(method);

            Object argValue = null;
            for (int i = 0; i < paramNames.length; i++) {
                if (paramNames[i].equals(exprParamNames[0])) {
                    argValue = argValues[i];
                    break;
                }
            }
            try {
                for (int i = 1; i < exprParamNames.length; i++) {
                    argValue = dealArg(argValue, exprParamNames[i]);
                }

                if (argValue instanceof String) {
                    return (String) argValue;
                }
            } catch (IllegalAccessException e) {
                log.info("keyExpr:{}" + keyExpr);
                throw new RuntimeException("无法解析表达式:" + e.getMessage());
            }
            throw new RuntimeException("无法解析表达式");
        } else {
            return keyExpr;
        }
    }

    private Object dealArg(Object object, String fieldName) throws IllegalAccessException {
        if (object == null) {
            return null;
        }
        Class clazz = object.getClass();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            //属性可见
            field.setAccessible(true);
            if (fieldName.equals(field.getName())) {
                return field.get(object);
            }
        }
        return null;
    }


    private String findCache(String key) {
        double k = Math.random();
        log.info("k======"+k);
        if (k < 0.5) {
            return key + "_lt_0.5";
        }
        return null;
    }

  • 测试
  @RequestMapping(value = "testDeptCache",method = RequestMethod.GET)
    @DeptCache(keyExpr = "#user.nickname")
    public String testDeptCache(User user) {
        return "OK";
    }

3.权限注解

  • 注解
/**
 * 权限注解
 *
 * @author A.keung
 * 2019/8/30 0030
 */
@Target({ ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PermissionCheck {
    String[] values() default {};
}
  • 拦截器
/**
 * 权限注解拦截器
 *
 * @author A.keung
 * 2019/8/30 0030
 */
public class PermissionCheckInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (!handler.getClass().isAssignableFrom(HandlerMethod.class)) {
            return true;
        }
        PermissionCheck permission = findPermissionCheck((HandlerMethod) handler);
        //无权限注解
        if (permission == null) {
            return true;
        }
        //获取注解中的值
        String[] values = permission.values();
        //获取用户权限
        Map<String, String> userPermission = findUserPermission();

        for (String value : values) {
            if (userPermission.containsKey(value)) {
                return true;
            }
        }
        throw new Exception("No Permission");
    }

    /**
     * 模拟获取权限集
     *
     * @return 权限集
     */
    private Map<String, String> findUserPermission() {
        Map<String, String> permissionMap = new HashMap<>();
        permissionMap.put("SELECT", "");
        permissionMap.put("UPDATE", "");
        permissionMap.put("DELETE", "");
        return permissionMap;
    }

    /**
     * 获取注解信息
     *
     * @param handlerMethod 方法对象
     * @return PermissionCheck注解
     */
    private PermissionCheck findPermissionCheck(HandlerMethod handlerMethod) {
        //方法上注解
        PermissionCheck permission = handlerMethod.getMethodAnnotation(PermissionCheck.class);
        if (permission == null) {
            //类上注解
            permission = handlerMethod.getBeanType().getAnnotation(PermissionCheck.class);
        }
        return permission;
    }

}
  • 配置
/**
 * Mvc配置类
 * 在SpringBoot2.0之后的版本中WebMvcConfigurerAdapter过时了
 * 采用以下方式:
 * 1.继承WebMvcConfigurationSupport类
 *        静态资源的访问的问题,在静态资源的访问的过程中,
 *        SpringBoot中的自动的配置会失效,不需要返回逻辑视图,可以选择继承此类
 *        继承WebMvcConfigurationSupport,若多个类继承该类,只会有一个类中的重写的方法执行
 * 2.实现WebMvcConfigurer接口
 * @author A.keung
 * 2019/8/30 0030
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry interceptorRegistry) {
        interceptorRegistry.addInterceptor(new PermissionCheckInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/index/**","/api/login");
    }

    @Override
    public void configurePathMatch(PathMatchConfigurer pathMatchConfigurer) {

    }

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer contentNegotiationConfigurer) {

    }

    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer asyncSupportConfigurer) {

    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer defaultServletHandlerConfigurer) {

    }

    @Override
    public void addFormatters(FormatterRegistry formatterRegistry) {

    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry resourceHandlerRegistry) {

    }

    @Override
    public void addCorsMappings(CorsRegistry corsRegistry) {

    }

    @Override
    public void addViewControllers(ViewControllerRegistry viewControllerRegistry) {

    }

    @Override
    public void configureViewResolvers(ViewResolverRegistry viewResolverRegistry) {

    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> list) {

    }

    @Override
    public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> list) {

    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> list) {

    }

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> list) {

    }

    @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> list) {

    }

    @Override
    public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> list) {

    }

    @Override
    public Validator getValidator() {
        return null;
    }

    @Override
    public MessageCodesResolver getMessageCodesResolver() {
        return null;
    }
}
  • 测试
  @PermissionCheck(values = "SELECT")
    @RequestMapping(value = "testPermissionCheck",method = RequestMethod.GET)
    public String testPermissionCheck() {
        return "OK";
    }

发布了29 篇原创文章 · 获赞 8 · 访问量 7001

猜你喜欢

转载自blog.csdn.net/weixin_42032199/article/details/100223853