文章目录
简单演示
在Java中,注解(Annotation)是一种元数据(metadata)的形式,它用于为Java代码(类、方法、变量、参数和包等)提供信息。这些注解可以在编译时被处理,也可以在运行时被反射机制读取。
要编写一个Java注解,你需要使用@interface关键字,而不是通常的interface关键字。以下是一个简单的Java注解定义的例子:
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, ElementType.TYPE}) // 注解可以应用于方法和类
public @interface MyAnnotation {
// 定义一个名为value的元素,类型为String,默认值为""
String value() default "";
// 还可以定义其他元素,例如
int number() default 0;
// 也可以定义枚举类型的元素
MyEnum type() default MyEnum.DEFAULT;
// 枚举类型的定义
enum MyEnum {
DEFAULT,
TYPE_A,
TYPE_B
}
}
在这个例子中,我们定义了一个名为MyAnnotation的注解,它有三个元素:value、number和type。这些元素都有默认值,所以在使用注解时可以省略它们。
注解的@Retention和@Target元注解用于指定注解的保留策略和可应用的目标。@Retention(RetentionPolicy.RUNTIME)表示这个注解在运行时仍然保留,这意味着你可以通过反射机制读取它。@Target用于指定这个注解可以应用于哪些Java元素,例如方法、类、字段等。在这个例子中,MyAnnotation可以应用于方法和类。
使用注解的示例:
@MyAnnotation(value = "example", number = 10, type = MyAnnotation.MyEnum.TYPE_A)
public class MyClass {
@MyAnnotation(value = "anotherExample")
public void myMethod() {
// 方法体
}
}
常用元注解
在Java中,编写自定义注解时,通常需要使用到几个基本的元注解(Meta-Annotation),这些元注解用于为自定义注解提供额外的信息或指定其行为。以下是这些元注解的详细解释:
@Target
说明注解可以被用在哪些元素上,例如类、方法、字段等。
取值:
- ElementType.TYPE(类、接口(包括注解类型)或enum声明)、
- ElementType.FIELD(字段,包括enum实例)、
- ElementType.METHOD(方法)、
- ElementType.PARAMETER(参数)、
- ElementType.CONSTRUCTOR(构造器)、
- ElementType.LOCAL_VARIABLE(局部变量)、
- ElementType.ANNOTATION_TYPE(另一个注解)、
- ElementType.PACKAGE(包)、
- ElementType.TYPE_PARAMETER(类型参数)、
- ElementType.TYPE_USE(类型使用声明)。
示例:
@Target(ElementType.METHOD)
public @interface MyAnnotation {
// ...
}
@Retention
定义了注解的生命周期,即注解在何时生效。
取值:RetentionPolicy.SOURCE(注解只保留在源码中,在编译时会被丢弃,不包含在编译后的class文件中)、RetentionPolicy.CLASS(注解被保留在class文件中,但JVM在运行时忽略,这是默认的保留策略)、RetentionPolicy.RUNTIME(注解被保留在class文件中,并且可以被JVM在运行时读取)。
示例:
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
// ...
}
@Documented
表示该注解会被javadoc和类似的工具记录,因此可以通过javadoc命令生成API文档时包含此注解。
示例:
@Documented
public @interface MyAnnotation {
// ...
}
@Inherited
表示如果一个注解类型被声明为@Inherited,那么它将被用于类声明时,这个注解类型将被自动地继承到子类中去。
示例:
@Inherited
public @interface MyAnnotation {
// ...
}
@Repeatable(Java 8引入)
- 表示该注解类型可以多次应用于同一个声明类型上。
- 需要定义一个容器注解(Containing Annotation)来存放多个相同的注解实例。
示例:
@interface MyAnnotations {
MyAnnotation[] value();
}
@Repeatable(MyAnnotations.class)
public @interface MyAnnotation {
String value() default "";
}
// 使用时
@MyAnnotation("annotation1")
@MyAnnotation("annotation2")
public class MyClass {
// ...
}
// 或者使用容器注解
@MyAnnotations({
@MyAnnotation("annotation1"),
@MyAnnotation("annotation2")
})
public class MyClass {
// ...
}
注意:在编写自定义注解时,并不是所有元注解都是必需的,通常你需要根据你的需求来决定使用哪些元注解。例如,如果你只是想在编译时检查某些代码,那么可能只需要@Target和@Retention(RetentionPolicy.CLASS)就足够了。如果你希望注解在运行时能够被读取,那么就需要@Retention(RetentionPolicy.RUNTIME)。
高级示例代码
在Java中,通过自定义注解和AspectJ或Spring
AOP实现AOP(面向切面编程)是一种常见的做法。下面我将给出一个基于Spring AOP的示例代码,并解释说明每一步的操作。
1. 定义自定义注解
首先,我们需要定义一个自定义注解,比如@MyAnnotation。
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 MyAnnotation {
// 可以定义一些属性,例如value、description等
String value() default "";
}
2. 编写切面类
然后,我们编写一个切面类,并使用@Aspect和@Component(如果集成在Spring中)注解。
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class MyAspect {
@Before("@annotation(myAnnotation)")
public void beforeAdvice(JoinPoint joinPoint, MyAnnotation myAnnotation) {
System.out.println("Method " + joinPoint.getSignature().getName() + " is about to execute with annotation value: " + myAnnotation.value());
}
}
在上面的代码中,@Before注解标识了一个前置通知,当目标方法被调用之前,会执行这个通知。@annotation(myAnnotation)是一个切点表达式,表示要拦截所有带有@MyAnnotation注解的方法。
3. 在目标类上使用注解
现在,我们可以在目标类的方法上使用我们定义的注解。
@Component
public class MyService {
@MyAnnotation(value = "Test Annotation")
public void doSomething() {
System.out.println("Doing something...");
}
}
4. 配置Spring AOP
在Spring项目中,需要启用AOP支持。这通常通过在配置类上使用@EnableAspectJAutoProxy注解来实现。
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan(basePackages = "com.example.aop") // 替换为你的包名
@EnableAspectJAutoProxy
public class AppConfig {
// ... 其他bean定义 ...
}
5. 运行和测试
现在,当你运行你的Spring应用程序并调用MyService的doSomething方法时,你应该会在控制台看到MyAspect的beforeAdvice方法打印出的日志信息。
6.解释说明
-
@MyAnnotation 是一个自定义注解,用于标记需要特殊处理的方法。
-
@Aspect 标记了一个类作为切面类,它包含了通知(Advice)和切点(Pointcut)的定义。
-
@Before 是一个通知注解,表示在方法执行之前执行某个操作。
-
@annotation(myAnnotation) 是一个切点表达式,它指定了要拦截的方法必须带有@MyAnnotation注解。
在切面类中,我们定义了一个前置通知方法beforeAdvice,当满足切点条件时,该方法会被执行。这个方法接收两个参数:一个是JoinPoint对象,用于访问被拦截方法的元数据;另一个是MyAnnotation对象,它是被拦截方法上的注解实例。
在目标类MyService中,我们在doSomething方法上使用了@MyAnnotation注解,并指定了一个值。
最后,我们需要在Spring配置中启用AOP支持,以便Spring容器能够扫描和注册切面类和通知。这通常通过@EnableAspectJAutoProxy注解来实现。