- 作 者:是江迪呀
- ✒️本文关键词:
Java
、高级应用
、自定义注解
、注解
- ☀️每日 一言:
追求潮流,其本身一点都不潮流!
使用 Java 注解(Annotations)可以在代码中添加元数据,提供了一种将元数据与代码关联的方式。注解可以在类、方法、字段等各种元素上使用,以提供附加的信息,这些信息可以被编译器、工具或者运行时使用。注解编程也是Java知识体系中很重要的一环。
在计算机编程中,注解(Annotation
)是一种为代码添加元数据(metadata
)的方式,用于提供关于代码的附加信息。注解可以用于类、方法、字段、参数等程序元素,以便在编译时、运行时或者使用特定工具时进行处理。注解是一种用于描述代码的元数据,它本身不会影响代码的逻辑运行,但可以为代码提供一些附加的信息。这些信息可以用于编译器的优化、代码分析、文档生成、测试等各种用途。
注解可以用来处理一些繁琐而具有共性的东西,比如日志添加、鉴权,可以有效的减少代码量、配置项从而提高代码的可读性、一致性。
注解一般分为预定义注解和自定义注解
Java
中有很多预定义注解,比如:
@Override
:标记方法覆盖父类方法。@Deprecated
:标记方法或类已过时,不推荐使用。@SuppressWarnings
:抑制编译器警告。@FunctionalInterface
:标记接口是一个函数式接口。Java 还支持自定义注解。自定义注解使用 @interface
关键字定义,并可以在注解内部定义元素。自定义注解可以带有默认值,也可以指定保留策略(源代码、类文件、运行时等)。
如果你想要自定义一个注解是很简单的,如下代码所示:
public @interface MyAnnotation {
// 定义一个属性名为 "value"
String value() default "";
// 定义一个属性名为 "count",并设置默认值
int count() default 1;
}
如果你想使用:
@MyAnnotation(value = "Hello", count = 3)
public class MyClass {
...
}
非常简单。
我们可以在自定义注解类上面加上元注解,那么有哪些元注解可以使用,他们的作用是什么?如下所示:
(1)@Retention
:这个元注解指定了自定义注解的保留策略,即注解在什么级别保留。它有以下取值:
RetentionPolicy.SOURCE
:注解仅保留在源代码中,编译后不包含在字节码中。RetentionPolicy.CLASS
:注解保留在字节码中,但不会在运行时被反射获取(默认值)。RetentionPolicy.RUNTIME
:注解保留在字节码中,并可以在运行时通过反射获取(实际场景中使用最多)。(2)@Target
: 这个元注解指定了自定义注解可以应用在哪些元素上,如果自定义的注解只能作用于方法上,那么就不能作用于类上。它有以下取值:
ElementType.TYPE
:作用于类、接口、枚举等类型。 ElementType.FIELD
:字段。ElementType.METHOD
:作用于方法。 ElementType.PARAMETER
:方法参数。ElementType.FIELD
:作用于属性。ElementType.PACKAGE
:用于包声明。ElementType.LOCAL_VARIABLE
:用于局部变量。ElementType.CONSTRUCTOR
:作用于构造函数。 ElementType.LOCAL_VARIABLE
:局部变量。ElementType.ANNOTATION_TYP
:作用于注解类型。 ElementType.PACKAGE
:包。ElementType.TYPE_PARAMETER
:作用于类型参数(Java 8+)。ElementType.TYPE_USE
:作用于类型使用(Java 8+)。(3)@Documented
: 这个元注解用于指定自定义注解是否应该包含在Java文档中。如果一个注解被标记为 @Documented,则它会出现在生成的API文档中。
(4)@Inherited
: 这个元注解用于指定自定义注解是否可以被继承。如果一个注解被标记为 @Inherited,则子类会继承父类上的相同注解。
我们有一个简单的用户类,我们想要为其中的某些方法添加一些说明文档。我们可以使用自定义注解来实现这个目标。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MethodDocumentation {
String value() default "";
}
import cn.trueland.anno.MethodDocumentation;
import lombok.Data;
@Data
public class User {
private String userId;
@MethodDocumentation("这个方法返回的是用户的姓名!")
public String getName() {
return "是江迪呀";
}
}
在其他地方使用反射来获取注解的信息:
import cn.trueland.anno.MethodDocumentation;
import cn.trueland.model.User;
import java.lang.reflect.Method;
public class TestRunMain {
public static void main(String[] args) {
User user = new User();
Method[] methods = user.getClass().getDeclaredMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(MethodDocumentation.class)) {
MethodDocumentation annotation = method.getAnnotation(MethodDocumentation.class);
System.out.println("Method: " + method.getName());
System.out.println("Documentation: " + annotation.value());
System.out.println();
}
}
}
}
结果:
Method: getName
Documentation: 这个方法返回的是用户的姓名!
如果单独使用自定义注解,能够实现的东西很少,但是如果加上AOP
那么应用的场景就很多了。
下面是自定义注解 + AOP
实现的一个记录日志的代码示例:
(1)日志实体类:
import lombok.Data;
@Data
public class Log {
/**
* 操作类型
*/
private String operationType;
/**
* 操作内容
*/
private String operationContent;
/**
* 操作人id
*/
private Long userId;
/**
* 操作人名称
*/
private String userName;
}
(2)自定义注解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogAnnotation {
/**
* 操作类型
*/
String OperationType() default "";
/**
* 操作内容
*/
String OperationContent() default "";
/**
* 操作人id
*/
long UserId() default 0;
/**
* 操作人名称
*/
String UserName() default "";
}
(3)将注解属性转为注解对象工具类:
public class AnnotationUtil {
public static <T extends Annotation> T getAnnotationMessageClass(JoinPoint joinPoint, Class<T> tClass) {
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (null == method) {
return null;
}
return method.getAnnotation(tClass);
}
}
(4)AOP
切面:
@Aspect
@Component
@Slf4j
public class LogAop {
//切面的路径就是你自定义注解的路径
@Pointcut("@annotation(cn.trueland.anno.LogAnnotation)")
public void pointCut(){}
//使用后置通知
@After("pointCut()")
public void beforeAop(JoinPoint joinPoint){
//获取日志对象
LogAnnotation logAnnotation = AnnotationUtil.getAnnotationMessageClass(joinPoint, LogAnnotation.class);
//处理日志入库逻辑
System.out.println("操作类型:" + logAnnotation.OperationType());
System.out.println("操作内容:" +logAnnotation.OperationContent());
System.out.println("操作人id:" +logAnnotation.UserId());
System.out.println("操作用户名:" +logAnnotation.UserName());
}
}
(5)具体的方法
@LogAnnotation(OperationType = "添加",OperationContent = "添加用户信息",UserId = 01,UserName="是江迪呀")
public void add(){
//业务逻辑
}
(6)运行结果:
操作类型:添加
操作内容:添加用户信息
操作人id:1
操作用户名:是江迪呀