Java:注解知识温顾

注解

1.1 什么是注解

  • 注解用来给类声明附加额外信息,可以标注在类、字段、方法上面,编译器、JVM以及代码中可以通过反射获取注解信息,进行做处理
  • 比如控制层主要方法添加的日志注解,可以通过日志注解中获取信息,然后保存记录日志数据,方便代码的调试

1.2 常用注解

  • @Override
    • 标注在子类重写的父类方法之上,具有提示作用
  • @Deprecated
    • 标注该类或者方法已过时,在后续版本中会被遗弃,具有提示作用
  • @SuppressWarning(“unchecked”)
    • 标注在编译器检测到有问题的类、方法等上面,用来取消编译器的警告提示,警告类型有serial、unchecked、unuserd、all

1.3 元注解

  • 用来在声明新的注解时,指定新注解的一些特性
  • @Target:指定注解标注的位置,比如类、字段、方法等
    • image-20210901183756027
    • TYPE:类、接口(包括注解)或者枚举声明
    • FIELD:字段声明(包括枚举常量)
    • PARAMETER:参数声明
    • CONSTRUCTOR:构造函数声明
    • LOCAL_VARIABLE:本地变量声明
    • ANNOTATION_TYPE:注解类型声明
    • PACKAGE:包声明
    • TYPE_PARAMETER:类型参数声明,java8引入,可以用于类的泛型声明的地方
    • TYPE_USE:java8引入,此类型包括类型声明和类型参数声明
  • @Retention:指定注解的信息保留到什么时候
    • image-20210902100532971
  • @Inherited:指定注解标注在父类上时,可以被子类继承
  • @Documented:指定注解将被javadoc记录

1.4 获取注解信息

  • 几个注解

    • @Target(ElementType.TYPE)
      @Retention(RetentionPolicy.RUNTIME)
      public @interface ClassAnnotation {
      
      }
      
    • 构造函数

      @Target(ElementType.CONSTRUCTOR)
      @Retention(RetentionPolicy.RUNTIME)
      public @interface ConstructorAnnotation {
      }
      
    • 属性

      @Target(ElementType.FIELD)
      @Retention(RetentionPolicy.RUNTIME)
      public @interface FieldAnnotation {
      }
      
    • 方法

      @Target(ElementType.METHOD)
      @Retention(RetentionPolicy.RUNTIME)
      public @interface MethodAnnotation {
      }
      
  • 测试类

    @ClassAnnotation
    public class Test {
    
        @FieldAnnotation
        private String name;
    
        @MethodAnnotation
        public void test() {
            System.out.println("test()");
        }
    
        @ConstructorAnnotation
        public Test() {
        }
    }
    
  • 测试代码

    public class GetAnnotationMessage {
    
        public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, NoSuchFieldException {
            // 获取class对象
            Class<?> clazz = Class.forName("com.fc.reflectionandannotation.annotation.message.Test");
            // 获取类上面的注解信息
            Annotation[] classAnnotation = clazz.getDeclaredAnnotations();
            Arrays.stream(classAnnotation).forEach(item -> {
                System.out.println(item);
            });
            // 获取字段上的注解信息
            Field field = clazz.getDeclaredField("name");
            Annotation[] fieldAnnotation = field.getDeclaredAnnotations();
            Arrays.stream(fieldAnnotation).forEach(item -> {
                System.out.println(item);
            });
            // 获取方法上的注解信息
            Method method = clazz.getDeclaredMethod("test", null);
            Annotation[] methodAnnotation = method.getDeclaredAnnotations();
            Arrays.stream(methodAnnotation).forEach(item -> {
                System.out.println(item);
            });
            // 获取构造方法上的注解信息
            Constructor<?> constructor = clazz.getDeclaredConstructor(null);
            Annotation[] declaredAnnotations = constructor.getDeclaredAnnotations();
            Arrays.stream(declaredAnnotations).forEach(item -> {
                System.out.println(item);
            });
        }
    }
    

1.5 注解如何生效

  • 实际项目中 注解想生效通过反射+aop机制

1.6 限流注解

1.6.1 第三方api
  • 使用google/guava实现

  • 引入pom.xml依赖

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>fclever</artifactId>
            <groupId>com.fcleverframework</groupId>
            <version>0.0.1-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>fclever-reflection-annotation</artifactId>
        <description>Reflection and annotation Project</description>
    
        <dependencies>
            <!--SpringBoot整合Web插件-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <!--aop切面-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
            </dependency>
            <!--guava-->
            <dependency>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
                <version>18.0</version>
            </dependency>
            <!--常用工具类包-->
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
                <version>3.12.0</version>
            </dependency>
        </dependencies>
    </project>
    
  • 搭建SpringBoot项目,通过控制层控制

    /**
     * @ClassName LimitController
     * @Description     限流控制层
     * @Author Fclever
     * @Date 2021/9/13 18:15
     **/
    @RestController
    public class LimitController {
    
        /**
         * 每秒产生2个令牌,一秒内只能访问该接口两次
         */
        private RateLimiter rateLimiter = RateLimiter.create(1.0);
    
        @GetMapping("/get")
        public String get() {
            // 判断是否可访问,true可 false不可
            boolean result = rateLimiter.tryAcquire();
            if (!result) {
                return "访问人数过多,请稍后重试!";
            }
            return "Fclever";
        }
    }
    
1.6.2 封装自定义限流注解
  • 思路

    • 限流注解
    • AOP环绕通知
      • 反射判断方法时候含有注解
      • 获取注解信息,支撑不同方法对限流规则的需求
  • 需要考虑的问题,使用该注解的方法可能所需的限流规则不一样,因此可以在注解中增设参数,支撑不用情景下的使用。

  • 代码

    /**
     * @ClassName FcleverCurrentLimit
     * @Description 自定义的限流注解
     * @Author Fclever
     * @Date 2021/9/22 16:14
     **/
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface CurrentLimit {
    
        /**
         * 进行限流的方法名称
         *
         * @return
         */
        String name() default "";
    
        /**
         * 该方法每秒能被访问的次数限制
         *  底层:令牌桶算法
         * @return
         */
        int token() default 10;
    }
    
  • 限流注解如何生效?注解已经编写完毕,直接在方法上是无法生效的,这时候需要借助AOP实现,Spring的AOP含有多种类型的通知,可以实现在方法前后等做处理拦截。

1.6.3 整合AOP实现限流
  • 补充知识:

    • 环绕通知中存在可以对目标方法进行调用,实现灵活的操作目标方法的执行情况,方法的返回值为目标方法的返回值。
    • 前置和后置无法直接控制调用目标方法。
    • 前置、后置、环绕同时存在,如果环绕不调用目标方法,则前置和后置失效。
    • 多种通知的执行顺序
      • 目标方法无异常
        • @Around
        • @Before
        • @AfterReturning
        • @After
        • @Around
      • 目标方法有异常
        • @Around
        • @Before
        • @AfterThrowing
        • @After
  • AOP代码

    /**
     * @ClassName CurrentLimitAop
     * @Description
     * @Author Fclever
     * @Date 2021/9/22 16:24
     **/
    @Aspect
    @Component
    public class CurrentLimitAop {
    
        // 存放不同方法上的含有限流注解的限流规则对象
        private ConcurrentHashMap<String, RateLimiter> rateLimiters = new ConcurrentHashMap<>();
    
        /**
         * 环绕通知
         *      只要在方法上添加了该限流注解,就可以被AOP的环绕通知拦截
         *      1. 通过反射判断方法上时候含有【限流注解】
         *      2. 若存在,获取限流注解中的【token】值,使用guava创建对应的RateLimiter,调用限流api
         */
        @Around(value = "@annotation(com.fc.reflectionandannotation.currentLimiting.annotation.CurrentLimit)")
        public Object around(ProceedingJoinPoint proceedingJoinPoint) {
            try {
                // 获取方法
                Signature signature = proceedingJoinPoint.getSignature();
                MethodSignature methodSignature = (MethodSignature) signature;
                // 获取方法上的指定注解,这里不要判断是否该方法上存在该注解,因为已经进入了环绕通知,肯定是存在注解的
                // 这里主要关注的是获取注解当中的信息
                CurrentLimit currentLimit = methodSignature.getMethod().getDeclaredAnnotation(CurrentLimit.class);
                String name = currentLimit.name();
                int token = currentLimit.token();
    
                RateLimiter rateLimiter = null;
                if(rateLimiters.containsKey(name)) {
                    rateLimiter = rateLimiters.get(name);
                } else {
                    rateLimiter = RateLimiter.create(token);
                    rateLimiters.put(name, rateLimiter);
                }
                if(!rateLimiter.tryAcquire()){
                    return "当前访问人数过多!";
                }
                // 返回值为目标方法返回值
                Object result = proceedingJoinPoint.proceed();
                return result;
            } catch (Throwable throwable) {
                return "方法报错";
            }
        }
    }
    
  • 控制层代码

    • image-20211214171753774

      }
            // 返回值为目标方法返回值
            Object result = proceedingJoinPoint.proceed();
            return result;
        } catch (Throwable throwable) {
            return "方法报错";
        }
      

      }
      }

    
    
  • 控制层代码

    • 在这里插入图片描述
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值