【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; }
-  自定义注解的处理: 
 可以创建自定义注解,并结合反射来控制程序的行为。例如,创建一个注解用于记录日志,或者控制某个方法的访问权限等。
小结:
“在注解上使用反射”就是通过反射机制,在程序运行时,动态地获取注解的值,并基于这些值来执行一些操作。反射提供了灵活的动态行为,注解则提供了丰富的元数据支持,两者结合能够实现很多灵活的功能,例如日志记录、事务管理、权限控制等。
