在学习的过程中,我对类和接口之外的一个特殊存在----注解,产生好奇,由此探索学习了一下。在本文中,我会通过一个自定义注解(结合SpringAOP)的案例来讲解注解的相关知识。
目录
1.JAVA元注解
Java 元注解(Meta-Annotation)是用于注解其他注解的特殊注解。它们定义了自定义注解的行为和约束。Java 提供了五个常见的元注解
1.1 @Target
@Target元注解是用于指定可以应用的目标元素
它的value值为ElementType.class
public enum ElementType {
//可以应用于类、接口(包括注解类型)或枚举声明
TYPE,
//可以应用于字段(成员变量)
FIELD,
//可以应用于方法声明
METHOD,
//可以应用于方法参数
PARAMETER,
//可以应用于构造函数声明
CONSTRUCTOR,
//可以应用于局部变量声明
LOCAL_VARIABLE,
//可以应用于注解类型(用于嵌套注解)
ANNOTATION_TYPE,
//可以应用于包声明
PACKAGE,
/**
* 注解可以应用于类型参数声明(例如泛型类T)
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* 注解可以应用于任何使用类型的地方
*
* @since 1.8
*/
TYPE_USE,
/**
* 注解可以应用于模块声明
*
* @since 9
*/
MODULE,
/**
* 注解可以应用于记录组件
*
* @jls 8.10.3 Record Members
* @jls 9.7.4 Where Annotations May Appear
*
* @since 16
*/
RECORD_COMPONENT;
}
1.2 @Retention
@Retention元注解指定注解的保留策略,即注解在什么阶段可见。
它的value值为RetentionPolicy.class类
public enum RetentionPolicy {
//注解只在源代码中可见,编译时会被丢弃
SOURCE,
//注解在字节码文件中存在,但运行时不可见(默认)
CLASS,
//注解在运行时保留,可通过反射访问
RUNTIME
}
1.3 @Documented
@Documented注解用于指定该注解是否会被javadoc或类似工具提取,若被@Documented注解修饰,那么该注解的元素会被包含在生成的各种文档内。(文档内会显示该类或方法被@Documented修饰的注解所修饰)
1.4 @Inherited
@Inherited注解用于指定某个注解是否可以被子类继承。如果父类使用了某个带有@Inherited的注解,那么子类也会继承该注解。这个元注解只能用于类级别的注解。
例如,Parent具备@MyAnnotation(带有@Inherited),那么当child类继承Parent类时,也会一并继承@MyAnnotation类。
1.5 @Repeatable
@Repeatable元注解允许同一个元素上多次使用同一类型的注解(jdk1.8引入)。为了使得注解可以重复,必须定义一个容器来持有多个相同类型的注解。
@Repeatable(MyAnnotations.class) //传入容器的字节码
public @interface MyAnnotation {
String value();
}
public @interface MyAnnotations {
MyAnnotation[] value();
}
使用方式:
@MyAnnotation("First")
@MyAnnotation("Second")
public class MyClass {
}
2. 自定义注解(结合AOP)
接下来我会通过一个自定义注解来计算方法耗时的例子来讲解注解的编写
2.1 依赖
<dependencies>
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
2.2 自定义注解
@Retention(RetentionPolicy.RUNTIME) //运行时保留
@Target({ElementType.TYPE, ElementType.METHOD}) //针对方法和类
public @interface MyTarget {
}
2.3 定义AOP切面类
@Component
@Slf4j
@Aspect
public class MyAnnotationAspect {
//切入点:注解标注在方法上
@Pointcut("@annotation(com.atguigu.test.target.MyTarget)")
public void annotatedMethod() {}
//切入点:注解标注在类上
@Pointcut("@within(com.atguigu.test.target.MyTarget)")
public void annotatedClass() {}
// 使用 @Around 记录方法耗时,适用于方法和类上的注解
@Around("annotatedMethod() || annotatedClass()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
long begin = System.currentTimeMillis();
log.info("进入方法");
// 执行目标方法
Object result = null;
try {
result = joinPoint.proceed();
} catch (Throwable throwable) {
log.error("方法执行异常"+throwable.getMessage());
throw throwable; // 将异常继续抛出
}
long duration = System.currentTimeMillis() - begin;
log.info("离开方法");
log.info("访问方法时长: " + duration + " 毫秒");
return result; // 返回目标方法的执行结果
}
}
2.3 测试
@RestController
public class MyController {
@MyTarget
@GetMapping("/myAnnotation/test")
public String test() {
return "test";
}
}
启动服务后,访问localhost:8080/myAnnotation/test
出现以下情况代表测试成功 再把@MyTarget注解移动到类上
重启服务后再次访问localhost:8080/myAnnotation/test
3. 总结
Java 元注解为自定义注解的行为提供了规则和约束,确保注解可以正确地应用、保留和处理。在实际开发中,理解元注解的用途可以帮助开发者创建功能强大的自定义注解。