一、什么是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定义注解,并添加元注解配置。示例如下:
@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实现动态拦截。
- 创建切面类:
@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,只需要引入这个依赖即可:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>2、反射处理
- 通过Class.getAnnotation()获取注解信息
- 反射处理注解,通过反射在运行时读取注解信息:
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、添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>2、定义自定义注解
//不带参数注解
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)
创建一个切面类,定义注解的逻辑(如计算方法执行时间)。
不带参数:
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;
}
}带参数:
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。
不带参数:
import org.springframework.stereotype.Service;
@Service
public class SampleService {
@LogExecutionTime
public void longRunningMethod() throws InterruptedException {
// 模拟耗时操作
Thread.sleep(2000);
}
}带参数:
@LogExecutionTime(message = "自定义耗时监控")
public void myMethod() {
// ...
}