【Java】Java元注解
@Target(ElementType.METHOD)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface OperatorLog {String source() default "WEB"; //日志操作来源 默认是web,还有socket的String model() default ""; //操作模块
}
这个代码中的 @Target
和 @Retention
是元注解(meta-annotations),它们用于指定自定义注解的使用规则和行为。元注解本身并不直接影响注解的功能,而是控制注解如何被应用和如何被处理。
元注解解析:
-
@Target
:- 作用:指定自定义注解可以应用于哪些程序元素。常见的元素类型有:
ElementType.METHOD
:表示该注解只能应用于方法。ElementType.TYPE
:表示该注解可以应用于类、接口、枚举等类型。ElementType.FIELD
:表示该注解可以应用于字段。ElementType.PARAMETER
:表示该注解可以应用于方法参数。ElementType.LOCAL_VARIABLE
:表示该注解可以应用于局部变量。ElementType.CONSTRUCTOR
:表示该注解可以应用于构造函数。ElementType.ANNOTATION_TYPE
:表示该注解可以应用于其他注解。ElementType.PACKAGE
:表示该注解可以应用于包。
在你的代码中,
@Target(ElementType.METHOD)
表示OperatorLog
注解只能应用于方法上。也就是说,你不能将该注解应用于字段或类上。 - 作用:指定自定义注解可以应用于哪些程序元素。常见的元素类型有:
-
@Retention
:- 作用:指定注解在什么时机可用。常见的保留策略有:
RetentionPolicy.SOURCE
:注解仅存在于源代码中,编译后会被丢弃。RetentionPolicy.CLASS
:注解会被编译器保留在.class
文件中,但在运行时不可用(默认行为)。RetentionPolicy.RUNTIME
:注解会被编译并保留在.class
文件中,运行时可通过反射读取。通常自定义注解都使用RUNTIME
保留策略,便于在运行时进行处理。
在你的代码中,
@Retention(value = RetentionPolicy.RUNTIME)
表示OperatorLog
注解在运行时依然可用,可以通过反射来访问。 - 作用:指定注解在什么时机可用。常见的保留策略有:
总结即为:
@Target
用来指定注解应用的场景,通常需要使用它来限制注解的应用范围。@Retention
用来指定注解的生命周期,通常也需要使用它来确保注解在编译后仍然可以被访问(例如通过反射)。- 其他元注解:除了
@Target
和@Retention
,Java 还提供了其他元注解,如:@Documented
:表示该注解会出现在 Javadoc 中,适用于文档化注解。@Inherited
:表示该注解可以被子类继承(仅对类注解有效)。@Conditional(HAEnabledCheck.class)
这是核心的元注解,表示 @HaEnabled 注解的启用依赖于条件判断。@Conditional 注解接收一个类(这里是 HAEnabledCheck)作为参数,只有当这个类的判断条件为 true 时,@HaEnabled 注解才会生效。
HAEnabledCheck 是一个条件类,通常是一个实现了 Condition 接口的类,它决定是否启用带有 @HaEnabled 注解的功能。
如果你希望在自定义注解上使用反射,或者限制注解应用的范围,就需要使用这些元注解。
注解和反射
在Java中,“在注解上使用反射”指的是通过反射机制在运行时动态地读取和处理注解的内容。反射是一种在程序运行时检查和操作类、方法、字段等的能力,而注解是一种用于在代码中添加元数据的机制。
反射和注解的关系
注解本身只是对代码的标记,它不会自动执行某些操作,反而需要程序在运行时通过反射去读取它,基于注解的信息做一些逻辑上的处理。例如,AOP(面向切面编程)就是通过反射读取注解,来决定是否在方法执行前、后或者抛出异常时执行某些逻辑。
如何通过反射读取注解
-
定义注解:
首先,你需要定义一个注解,并在注解上使用元注解(如@Target
,@Retention
)来指定注解的使用规则和生命周期。示例:
@Retention(RetentionPolicy.RUNTIME) // 保留到运行时 @Target(ElementType.METHOD) // 只允许用于方法 public @interface MyAnnotation {String value() default "defaultValue"; // 注解中的属性 }
-
在代码中使用注解:
然后,你可以在代码中的方法上使用该注解,来标记某些方法。示例:
public class MyClass {@MyAnnotation(value = "Hello, World!")public void myMethod() {System.out.println("Executing myMethod...");} }
-
通过反射读取注解:
接下来,通过反射,你可以获取方法上的注解信息,并根据注解的内容来执行相应的操作。为了读取注解,你可以使用Method
类的getAnnotation
方法。示例:
import java.lang.annotation.Annotation; import java.lang.reflect.Method;public class AnnotationExample {public static void main(String[] args) throws Exception {// 获取 MyClass 类的字节码对象Class<MyClass> clazz = MyClass.class;// 获取 myMethod 方法的 Method 对象Method method = clazz.getMethod("myMethod");// 检查该方法是否有 MyAnnotation 注解if (method.isAnnotationPresent(MyAnnotation.class)) {// 获取 MyAnnotation 注解MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);// 读取注解中的值System.out.println("Annotation value: " + annotation.value());}// 执行方法MyClass obj = new MyClass();obj.myMethod();} }
-
运行结果:
在运行时,程序会读取myMethod
上的MyAnnotation
注解,并输出注解中的value
属性内容,执行结果如下:Annotation value: Hello, World! Executing myMethod...
为什么需要在注解上使用反射?
反射与注解的结合非常强大,它能够让你在程序运行时根据注解的内容动态地改变程序行为,这就是所谓的“注解驱动编程”。
常见的应用场景有:
-
AOP(面向切面编程):
使用注解标记方法,然后通过反射获取注解的值,执行一些逻辑(如日志、权限检查、事务控制等)对方法进行增强。比如Spring中的@Transactional注解,使用反射来判断方法是否需要事务支持。@Transactional public void someMethod() {// 业务逻辑 }
-
依赖注入(DI):
在Spring等框架中,通过注解(如@Autowired
或@Inject
)标记字段或方法,框架通过反射读取这些注解并自动注入依赖。@Autowired private MyService myService;
-
ORM(对象关系映射)框架:
在ORM框架中,注解可以标记实体类与数据库表的映射关系,框架通过反射读取注解,在数据库中进行查询、插入等操作。@Entity public class User {@Idprivate Long id;private String name; }
-
自定义注解的处理:
可以创建自定义注解,并结合反射来控制程序的行为。例如,创建一个注解用于记录日志,或者控制某个方法的访问权限等。
小结:
“在注解上使用反射”就是通过反射机制,在程序运行时,动态地获取注解的值,并基于这些值来执行一些操作。反射提供了灵活的动态行为,注解则提供了丰富的元数据支持,两者结合能够实现很多灵活的功能,例如日志记录、事务管理、权限控制等。