Skip to content

一、什么是java注解?

注解(Annotation)是Java 5.0引入的一种元数据形式,它提供了一种向代码添加元信息的方式。
注解本身不会直接影响代码逻辑,但可以被编译器、开发工具或运行时环境读取和使用。

二、java原生注解

Java中的原生注解(即JDK内置的注解),主要用于代码的元数据标记和编译、运行时的行为控制。主要分为以下几类:基础注解(用于代码标记)、元注解(用于定义其他注解)、其他原生注解。 作用域:所有注解均位于java.lang或java.lang.annotation包。排除项:如@Test(JUnit)、@WebServlet(Java EE)等属于第三方或扩展库,非JDK原生。

元注解(用于定义其他注解)

@Retention:控制注解的生命周期(通过RetentionPolicy枚举)
@Target:指定注解可应用的位置(通过ElementType枚举)
@Documented:将注解包含在Javadoc中
@Inherited:允许子类继承父类的类级别注解
@Repeatable(Java 8+):允许同一注解多次应用于同一位置

基础注解(用于代码标记)

@Override:标记方法重写父类或接口的方法,编译器会检查方法签名是否正确。
@Deprecated:标记方法、类或字段已过时,使用时编译器会生成警告。
@SuppressWarnings:抑制编译器警告(如"unchecked"或"deprecation")。
@SafeVarargs (Java 7+):标记方法或构造器的可变参数使用是安全的,抑制堆污染警告。
@FunctionalInterface (Java 8+):标记接口为函数式接口,确保只有一个抽象方法。

其他原生注解

@Native (Java 8+):标记字段可能被本地代码(如JNI)引用,通常用于常量字段。
@Generated (Java 9+):标记由代码生成工具生成的代码(位于javax.annotation.processing包,但属于JDK内置)。

三、如何自定义注解:

1、@Interface

使用@interface定义注解,并添加元注解配置。示例如下:

java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestCase {
    String description() default "默认描述";
    int priority() default 1;
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Author {
    String value();
}
例:使用注解
public class TestSuite {
    @TestCase(description = "登录测试", priority = 2)
    public void testLogin() {
        // 测试代码
    }
}

@Author("John") // 等价于@Author(value="John")
public class MyClass {}

注解参数规则:

  • 参数类型:仅允许基本类型、String、Class、枚举、注解及其数组.
  • 注解参数不能为null,且不支持泛型.
  • 默认值:使用default关键字,不能为null.
  • 特殊参数value:若唯一参数为value,使用时可以省略参数名.

2、处理注解逻辑

1、AOP处理(推荐) 结合Spring AOP实现动态拦截。

  • 创建切面类:
java
@Aspect
@Component
public class LogAspect {
    @Around("@annotation(LogExecutionTime)")
    public Object logTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long duration = System.currentTimeMillis() - start;
        System.out.println("方法执行耗时:" + duration + "ms");
        return result;
    }
}
  • 启用AOP支持: 在启动类添加@EnableAspectJAutoProxy。在 Spring Boot 3 及以上版本中,默认已启用 AOP 自动代理,无需手动添加 @EnableAspectJAutoProxy,只需要引入这个依赖即可:
java
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2、反射处理

  • 通过Class.getAnnotation()获取注解信息
  • 反射处理注解,通过反射在运行时读取注解信息:
java
Method method = TestSuite.class.getMethod("testLogin");
TestCase testCase = method.getAnnotation(TestCase.class);
if (testCase != null) {
    System.out.println("描述: " + testCase.description());
    System.out.println("优先级: " + testCase.priority());
}

四、示例: springboot3自定义注解

1、添加依赖

java
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2、定义自定义注解

java
//不带参数注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD) // 注解作用于方法
@Retention(RetentionPolicy.RUNTIME) // 注解在运行时可见
public @interface LogExecutionTime {
}

//带参数注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {
    String message() default "Execution time"; // 默认参数
}

//带参数注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD) // 注解作用在方法上
@Retention(RetentionPolicy.RUNTIME) // 运行时保留
public @interface CustomAnnotation {
    String value() default ""; // 可自定义属性
}

3、编写切面类(Aspect)

创建一个切面类,定义注解的逻辑(如计算方法执行时间)。
不带参数:

java
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogExecutionTimeAspect {

    // 定义切入点:所有被@LogExecutionTime注解的方法
    @Around("@annotation(LogExecutionTime)")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        
        // 执行目标方法
        Object result = joinPoint.proceed();
        
        long endTime = System.currentTimeMillis();
        System.out.println(joinPoint.getSignature() + " executed in " + (endTime - startTime) + "ms");
        
        return result;
    }
}

带参数:

java

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect // 声明为切面类
@Component // 纳入Spring容器管理
public class CustomAnnotationAspect {

    // 定义切点:拦截所有被@CustomAnnotation注解的方法
    @Around("@annotation(customAnnotation)")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint, CustomAnnotation customAnnotation) throws Throwable {
        // 获取注解属性值
        String annotationValue = customAnnotation.value();
        
        // 前置处理(示例:记录开始时间)
        long startTime = System.currentTimeMillis();
        System.out.println("【注解值】: " + annotationValue);
        System.out.println("【方法开始】: " + joinPoint.getSignature().getName());

        // 执行目标方法
        Object result = joinPoint.proceed();

        // 后置处理(示例:计算耗时)
        long duration = System.currentTimeMillis() - startTime;
        System.out.println("【方法结束】耗时: " + duration + "ms");

        return result;
    }

@Around("@annotation(logExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint, LogExecutionTime logExecutionTime) throws Throwable {
    long startTime = System.currentTimeMillis();
    Object result = joinPoint.proceed();
    long duration = System.currentTimeMillis() - startTime;
    
    // 获取注解参数
    String message = logExecutionTime.message();
    System.out.println(message + ": " + duration + "ms");
    
    return result;
}
}

4、在业务代码中使用注解

在需要监控执行时间的方法上添加@LogExecutionTime。
不带参数:

java
import org.springframework.stereotype.Service;

@Service
public class SampleService {
    
    @LogExecutionTime
    public void longRunningMethod() throws InterruptedException {
        // 模拟耗时操作
        Thread.sleep(2000);
    }
}

带参数:

java
@LogExecutionTime(message = "自定义耗时监控")
public void myMethod() {
    // ...
}

Released under the MIT License.