一、简介
- 注解:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
- 作用分类:
①编写文档:通过代码里标识的元数据生成文档【生成文档doc文档】
②代码分析:通过代码里标识的元数据对代码进行分析【使用反射】
③编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查【Override】 - 自定义注解:使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。规定注解里面只能使用java八大基本数据类型和String、enum、Annotation、class。
- 自定义注解格式:public @interface 注解名 {定义体}
二、原理
注解本质是一个继承了Annotation 的特殊接口,其具体实现类是Java运行时生成的动态代理类。而我们通过反射获取注解时,返回的是Java 运行时生成的动态代理对象$Proxy1。通过代理对象调用自定义注解(接口)的方法,会最终调用AnnotationInvocationHandler的invoke方法。该方法会从memberValues这个Map中索引出对应的值。而memberValues的来源是Java常量池。
三、元注解
4种元注解:@Target @Retention @Documented @Inherited
- @Target
@Target(ElementType.TYPE) //用于Class, interface (including annotation type), or enum
@Target(ElementType.FIELD) //用于Field (includes enum constants)
@Target(ElementType.METHOD) //用于Method
@Target(ElementType.PARAMETER) //用于Formal parameter正式参数
@Target(ElementType.CONSTRUCTOR) //用于Constructor方法
@Target(ElementType.LOCAL_VARIABLE) //用于局部变量
@Target(ElementType.ANNOTATION_TYPE) //用于注释类型
@Target(ElementType.PACKAGE) //用于Package
@Target(ElementType.TYPE_PARAMETER) //ype parameter declaration用于类型参数(@since jdk1.8)
@Target(ElementType.TYPE_USE) //Use of a type使用类型(@since jdk1.8) - @Retention
@Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在class字节码文件中不包含
@Retention(RetentionPolicy.CLASS) // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,
@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到 - @Document:该注解将被包含在javadoc中
- @Inherited:子类可以继承父类中的该注解
四、实例:自定义注解的实现打印日志
-
导入aop相关jar包
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
-
定义注解接口
import java.lang.annotation.*; @Documented //注解是否将包含在JavaDoc中 @Target(ElementType.METHOD) //注解用于什么地方 @Retention(RetentionPolicy.RUNTIME) //定义该注解的生命周期 public @interface LogAnnotation { }
-
配置aop拦截切面类
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; import java.util.Arrays; import java.util.Date; @Component @Aspect public class LogAspect { //切入点签名 @Pointcut("@annotation(com.cloud.ribbon.consumer.annotation.LogAnnotation)") private void cutPoint() { } //前置通知 @Before("cutPoint()") public void beforeCutPoint() { } //环绕通知 @Around(value = "cutPoint()") public Object intercept(ProceedingJoinPoint joinPoint) { Object result = null; //类名 String calssName = joinPoint.getTarget().getClass().getName(); System.out.println("类名:" + calssName); //方法名 String methodName = joinPoint.getSignature().getName(); System.out.println("方法名:" + methodName); //参数 Object[] args = joinPoint.getArgs(); System.out.println("参数:" + Arrays.asList(args).toString()); //方法开始时间 Date date = new Date(); System.out.println("方法开始时间:" + date); //方法返回值 try { result = joinPoint.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } System.out.println("方法返回值:" + result); if (result.toString().contains("bbb")){ result = "修改后的值bbbbb"; } System.out.println("方法修改后的返回值:" + result); //方法结束时间 Date date2 = new Date(); System.out.println("方法结束时间:" + date2); return result; } //后置通知 @After("cutPoint()") public void afterCutPoint() { } }
-
使用注解
import com.cloud.ribbon.consumer.annotation.LogAnnotation; import com.cloud.ribbon.consumer.service.HelloService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class ConsumerController { @Autowired HelloService helloService; @LogAnnotation @GetMapping(value = "/index") public String index() { return helloService.sayHello()+ "bbbbbb"; } }
-
控制台输出结果
方法名:index 参数:[] 方法开始时间:Tue Dec 24 15:42:19 CST 2019 方法返回值:查询失败bbbbbb 方法修改后的返回值:修改后的值bbbbb 方法结束时间:Tue Dec 24 15:42:20 CST 2019
备注:service层代码没有贴上来,可根据自己实际情况使用注解