Dos formas de personalizar las anotaciones de Java

Dos formas de personalizar las anotaciones de Java

Las anotaciones personalizadas son una implementación altamente implementada de eliminación de código público. Aquí hay dos métodos para las anotaciones personalizadas. Método de interceptor y método de superficie de corte .

Primero, el método interceptor

①Introduzca anotaciones personalizadas en el controlador (esquema simple)

@RequestMapping("/validate")
@Validation(namelength = 3,age = 18,gender="女")
    public String test(@RequestParam Map<String,Object> map){
        System.out.println("骑上我心爱的小摩托,再也不会堵车!");
        return "ok";
    }

@ Validación aquí es una anotación personalizada
② escriba una anotación personalizada (no hay requisitos de pedido con el primer paso)

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Validation {
    int namelength();
    int age();
    String gender();
}

El parámetro aquí es el parámetro pasado cuando se llama a la anotación. No puede escribir el parámetro. En ese caso, puede determinar directamente el parámetro de solicitud. Si lo escribe, es conveniente para el control, como la longitud del parámetro de solicitud ...

③ Verificar los parámetros en el interceptor

@Component
public class MyValidInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod hm = (HandlerMethod) handler;
            Validation validation = hm.getMethodAnnotation(Validation.class);
            if (null == validation) {
                return true;
            }
            //获取自定义注解上的传的参数(没传就不用获取)
            int namelength = validation.namelength();
            int ageLength = validation.age();
            String annogender = validation.gender();
            //如果是@RequestParam接收的参数,集合形式的用这种办法获取
            Map<String, Object> map = getParameterMap(request);
            //如果是@RequestBody接收参数的,对象形式的用这种办法获取,因为要用流的形式(getHeader()或getInputStream())去获取请求参数,而控制层的@RequestBody也是根据流获取对象,而request的流只能被调一次,所以这里接收对象不适合用拦截器
//            User u = getRequestBodyParam(request,User.class);
//            String name = u.getName();
//            int age = u.getAge();
//            String gender = u.getGender();
//
            String name = null;
            if (null != map.get("name")) {
                name = (String) map.get("name");
            }
            ;
            Integer age = null;
            if (null != map.get("age")) {
                age = Integer.parseInt((String) map.get("age"));
            }
            String gender = null;
            if (null != map.get("gender")) {
                gender = (String) map.get("gender");
            }
            //这里就方便对请求参数做限制,不同的地方调自定义注解可以传不同的注解参数,那就是实现不同接口对请求参数要求不一致
            int paramlength = name.length();
            String str = "";
            if (namelength > paramlength) {
                str = "名字长度不能小于:" + namelength;
                render(response, str);
                return false;
            }
            if (age < ageLength) {
                str = "年龄不能小于:" + ageLength;
                render(response, str);
                return false;
            }
            if (!annogender.equalsIgnoreCase(gender)) {
                str = "你的性别不是:" + annogender;
                render(response, str);
                return false;
            }
            return true;
        }
        return true;
    }

    /**
     * 从request中获取@RequestBody传过来的参数
     *
     * @param request
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> T getRequestBodyParam(HttpServletRequest request, Class<T> clazz) {
        String body = null;
        try {
            body =
                    request.
                            getReader().
                            lines().
                            collect(Collectors.joining(System.lineSeparator()));
        } catch (IOException e) {
            e.printStackTrace();
        }
        T myParam = JSON.parseObject(body, clazz);
        return myParam;
    }

    /**
     * 从request中获取@RequestParam传过来的参数
     *
     * @return
     * @RequestParam 获取的参数
     * 获取所有请求参数,
     * 封装为map对象
     */
    public Map<String, Object> getParameterMap(HttpServletRequest request) {
        if (request == null) {
            return null;
        }
        Enumeration<String> enumeration = request.getParameterNames();
        Map<String, Object> parameterMap = new HashMap<String, Object>();
        StringBuilder stringBuilder = new StringBuilder();
        while (enumeration.hasMoreElements()) {
            String key = enumeration.nextElement();
            String value = request.getParameter(key);
            String keyValue = key + " : " + value + " ; ";
            stringBuilder.append(keyValue);
            parameterMap.put(key, value);
        }
        return parameterMap;
    }

    private void render(HttpServletResponse response, String str) throws Exception {
        response.setContentType("application/json;charset=UTF-8");
        OutputStream out = response.getOutputStream();
        out.write(str.getBytes("UTF-8"));
        out.flush();
        out.close();
    }
}

④ Registre el interceptor en WebConfig

@Configuration
public class WebConfig implements WebMvcConfigurer {
    //关键,将拦截器作为bean写入配置中,否则自定义拦截器中无法注入spring管理的bean
    @Bean
    public MyValidInterceptor myInterceptor(){
        return new MyValidInterceptor();
    }
    // 添加自定义拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(myInterceptor());
    }
}

Aquí, se implementa la verificación de parámetros del interceptor personalizado, y las condiciones de restricción pueden pasar diferentes parámetros de anotación de acuerdo con diferentes necesidades.

En segundo lugar, el método de corte.

Use planos de corte para hacer anotaciones personalizadas (recomendado)
① Invoque en la capa de control (haga una anotación general para limitar la corriente de la interfaz)

@RestController
@RequestMapping("/aspect")
public class AnnotationAspectController {
    @RequestMapping("/annotation")
    @TimesValidate(seconds = 60,maxCount = 5)
    public void test(){
        System.out.println("人间值得");
    }
}

@TimesValidate (segundos = 60, maxCount = 5) es una anotación personalizada,
segundos y maxCount son los parámetros de anotación pasados
② escriba una anotación (no se requiere secuencia con el primer paso)

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TimesValidate {
    int seconds();
    int maxCount();
}

③ escribir clase de aspecto (redis utilizado aquí para acceder a los datos)

@Component
@Aspect
public class TimesValidateAspect {
    @Autowired
    protected RedisService redisService;

    private static final String SAVESECOND = "TIMES_5SECOND";

    @Pointcut("@annotation(com.sinux.icoding.selfannotation.annotation.TimesValidate)")
    public void pointcut() {

    }
 /**
         *环绕:在目标类执行前后都会执行这个方法,执行前,
         * @around会执行到proceedingJoinPoint.proceed();
         *然后线程阻塞
         *在目标方法执行完毕、后又接着proceedingJoinPoint.proceed()往下执行。
         *他应该保持线程安全
         * 注意他应该在不满足条件 return false之后,因为在他之前的话,
         * 那不管满足条件与否都会执行目标方法就是去了作用
         **/
    @Around("pointcut()")
    public Object validateTimes(ProceedingJoinPoint joinPoint){
        //取参数
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        //获取request和response
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) requestAttributes).getResponse();
        TimesValidate timesValidate = method.getAnnotation(TimesValidate.class);
        int seconds = timesValidate.seconds();
        int maxcount = timesValidate.maxCount();

        boolean flag = checkAccessTimes(maxcount,seconds);
        String str = seconds+"秒内,访问接口次数不能超过"+maxcount+"次!";

        if(!flag){
            render(response,str);
            return false;
        }
       /**
       *这里需要注意这个joinPoint.proceed();的位置,他要写在校验不通过的后面,因为执行目标方法前会先执行到这里阻塞,目标方法执行完后继续从这里执行。在目标方法执行前就要给他拦住,所以这个joinPoint.proceed()节点要在拦截之后。
       **/
        Object result = null;
        try {
            result = joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return result;
    }

    public boolean checkAccessTimes(int maxcount,int seconds) {
        Integer data = redisService.getValue(SAVESECOND,Integer.class);
        Integer times = null;
        if (null != data ) {
            times = data;
        }

        if (null == times) {
            redisService.setCacheValueForTimes(SAVESECOND,1,seconds,TimeUnit.SECONDS);
            long m  = redisService.getValue(SAVESECOND,Integer.class);
            System.out.println(seconds+"秒内第"+m+"次访问接口!");
        } else if (times < maxcount) {
            long t = redisService.testInckey(SAVESECOND);
            System.out.println(seconds+"秒内第"+t+"次访问接口!");
        } else {
            return false;
        }
        return true;
    }
    private void render(HttpServletResponse response, String str) {
        response.setContentType("application/json;charset=UTF-8");
        OutputStream out = null;
        try {
             out = response.getOutputStream();
            out.write(str.getBytes("UTF-8"));
            out.flush();

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Esta realización de los objetivos dentro de los 60 segundos de conseguir la interfaz no es más de 5 veces, puede obtener ip en el proyecto como un ReDiS la clave, por lo que puede restringir el comportamiento de las interfaces de pincel maliciosos
aquí antes sobre el uso de texto encapsulado ha ReDiS hablando, ya no se Repetir

67 artículos originales publicados · Me gusta12 · Visitantes más de 10,000

Supongo que te gusta

Origin blog.csdn.net/m0_37635053/article/details/105416102
Recomendado
Clasificación