Skip to content
清晨的一缕阳光
返回

Java 注解体系详解

Java 注解体系详解

Java 注解不仅是元数据,更是框架设计的核心机制。

一、注解基础

1.1 什么是注解

// 内置注解
@Override
public String toString() {
    return "Hello";
}

@Deprecated
public void oldMethod() {}

@SuppressWarnings("unchecked")
public void genericMethod() {}

1.2 元注解

// @Target:注解作用目标
@Target(ElementType.METHOD)  // 只能用于方法
@Target({ElementType.FIELD, ElementType.METHOD})  // 多个目标

// @Retention:保留策略
@Retention(RetentionPolicy.SOURCE)   // 源码阶段丢弃
@Retention(RetentionPolicy.CLASS)    // 类加载时丢弃(默认)
@Retention(RetentionPolicy.RUNTIME)  // 运行时可用(反射)

// @Documented:是否包含在 Javadoc
@Documented

// @Inherited:是否可继承
@Inherited

二、自定义注解

2.1 定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogExecution {
    // 属性定义
    String value() default "";  // value 属性可省略名称
    boolean logParams() default true;
    boolean logResult() default false;
}

2.2 使用注解

public class UserService {
    
    @LogExecution("创建用户")
    public User createUser(String name) {
        // ...
    }
    
    @LogExecution(value = "删除用户", logParams = false)
    public void deleteUser(Long id) {
        // ...
    }
}

2.3 注解处理器

@SupportedAnnotationTypes("com.example.LogExecution")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class LogExecutionProcessor extends AbstractProcessor {
    
    @Override
    public boolean process(Set<? extends TypeElement> annotations,
                          RoundEnvironment roundEnv) {
        for (Element element : roundEnv.getElementsAnnotatedWith(LogExecution.class)) {
            LogExecution annotation = element.getAnnotation(LogExecution.class);
            String methodName = element.getSimpleName().toString();
            
            // 生成代码或验证
            processingEnv.getMessager().printMessage(
                Diagnostic.Kind.NOTE,
                "Found @LogExecution on method: " + methodName
            );
        }
        return true;
    }
}

三、运行时注解处理

3.1 反射读取注解

public class AnnotationReader {
    public static void readAnnotations(Method method) {
        if (method.isAnnotationPresent(LogExecution.class)) {
            LogExecution annotation = method.getAnnotation(LogExecution.class);
            System.out.println("Value: " + annotation.value());
            System.out.println("LogParams: " + annotation.logParams());
        }
    }
}

3.2 AOP 实现

@Aspect
public class LoggingAspect {
    
    @Around("@annotation(logExecution)")
    public Object logExecution(ProceedingJoinPoint pjp, 
                               LogExecution logExecution) throws Throwable {
        long start = System.currentTimeMillis();
        
        Object result = pjp.proceed();
        
        long duration = System.currentTimeMillis() - start;
        System.out.println(logExecution.value() + 
                          " executed in " + duration + "ms");
        
        return result;
    }
}

四、APT(注解处理器)

4.1 编译时代码生成

@SupportedAnnotationTypes("com.example.Builder")
public class BuilderProcessor extends AbstractProcessor {
    
    @Override
    public boolean process(Set<? extends TypeElement> annotations,
                          RoundEnvironment roundEnv) {
        for (Element element : roundEnv.getElementsAnnotatedWith(Builder.class)) {
            TypeElement typeElement = (TypeElement) element;
            String className = typeElement.getQualifiedName().toString();
            
            // 生成 Builder 类
            generateBuilderClass(typeElement, className);
        }
        return true;
    }
    
    private void generateBuilderClass(TypeElement element, String className) {
        // 使用 JavaPoet 生成代码
        TypeSpec builder = TypeSpec.classBuilder(className + "Builder")
            .addMethod(MethodSpec.methodBuilder("build")
                .returns(ClassName.get(element))
                .addCode("return new " + className + "();")
                .build())
            .build();
        
        // 写入文件
        // ...
    }
}

4.2 Lombok 原理

// Lombok 注解
@Data
@AllArgsConstructor
public class User {
    private Long id;
    private String name;
}

// 编译后生成
public class User {
    private Long id;
    private String name;
    
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    
    // equals, hashCode, toString 等
}

五、最佳实践

5.1 注解设计原则

// ✅ 推荐:简洁明了
@Cacheable(ttl = 3600)

// ❌ 避免:过于复杂
@Cacheable(key = "#id", 
           condition = "#id != null && #id > 0",
           unless = "#result == null",
           sync = true,
           cacheManager = "primaryCacheManager")

5.2 注解组合

// 创建组合注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Transactional
@Cacheable
@LogExecution
public @interface BusinessOperation {
    String value() default "";
}

// 使用
@BusinessOperation("创建订单")
public Order createOrder(OrderRequest request) {
    // ...
}

六、总结

注解核心要点:

特性说明应用场景
元注解定义注解的注解自定义注解
运行时注解反射读取AOP、框架配置
编译时注解APT 处理代码生成、验证
组合注解多个注解组合简化配置

注解是 Java 框架的基石,理解其原理有助于更好地使用 Spring、Lombok 等框架。


分享这篇文章到:

上一篇文章
Gin 框架核心原理
下一篇文章
Spring Boot 动态多数据源配置