Bootstrap

java如何编写一个注解

简单演示

在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注解来实现。

;