Two ways to customize java annotations

Two ways to customize java annotations

Custom annotations are a highly implemented implementation of public code stripping. Here are two methods for custom annotations. Interceptor method and cut surface method .

First, the interceptor method

①Introduce custom annotations in the controller (simple schematic)

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

@Validation here is a custom annotation
② write a custom annotation (no order requirements with the first step)

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

The parameter here is the parameter passed when the annotation is called. You can not write the parameter. In that case, you can directly determine the request parameter. If you write it, it is convenient for control, such as the length of the request parameter ...

③ Verify the parameters in the 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();
    }
}

④ Register the interceptor to 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());
    }
}

Here, the parameter verification of the custom interceptor is implemented, and the restriction conditions can pass different annotation parameters according to different needs.

Second, the cut method

Use cut planes to make custom annotations (recommended)
① Invoke in the control layer (make a general annotation for interface current limiting)

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

@TimesValidate (seconds = 60, maxCount = 5) is a custom annotation,
seconds and maxCount are the annotation parameters passed
② write an annotation (no sequence requirement with the first step)

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

③ write aspect class (redis used here to access data)

@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();
            }
        }
    }
}

This achieves the realization that the target interface logs in no more than 5 times within 60 seconds. In the project, you can obtain ip as the key of redis, which can limit the malicious behavior of the interface.
Here, the use of redis is encapsulated. Repeat.

Published 67 original articles · Liked12 · Visitors 10,000+

Guess you like

Origin blog.csdn.net/m0_37635053/article/details/105416102