注解
1.1 什么是注解
- 注解用来给类声明附加额外信息,可以标注在类、字段、方法上面,编译器、JVM以及代码中可以通过反射获取注解信息,进行做处理
- 比如控制层主要方法添加的日志注解,可以通过日志注解中获取信息,然后保存记录日志数据,方便代码的调试
1.2 常用注解
- @Override
- 标注在子类重写的父类方法之上,具有提示作用
- @Deprecated
- 标注该类或者方法已过时,在后续版本中会被遗弃,具有提示作用
- @SuppressWarning(“unchecked”)
- 标注在编译器检测到有问题的类、方法等上面,用来取消编译器的警告提示,警告类型有serial、unchecked、unuserd、all
1.3 元注解
- 用来在声明新的注解时,指定新注解的一些特性
- @Target:指定注解标注的位置,比如类、字段、方法等
- TYPE:类、接口(包括注解)或者枚举声明
- FIELD:字段声明(包括枚举常量)
- PARAMETER:参数声明
- CONSTRUCTOR:构造函数声明
- LOCAL_VARIABLE:本地变量声明
- ANNOTATION_TYPE:注解类型声明
- PACKAGE:包声明
- TYPE_PARAMETER:类型参数声明,java8引入,可以用于类的泛型声明的地方
- TYPE_USE:java8引入,此类型包括类型声明和类型参数声明
- @Retention:指定注解的信息保留到什么时候
- @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 "方法报错"; } } }
-
控制层代码
-
} // 返回值为目标方法返回值 Object result = proceedingJoinPoint.proceed(); return result; } catch (Throwable throwable) { return "方法报错"; }
}
}
-
-
控制层代码