背景

本文是《Java 后端从小白到大神》修仙系列第十七篇,正式进入Java后端世界,本篇文章主要聊Java基础。若想详细学习请点击首篇博文,我们开始把。

文章概览

  1. 注解

注解

1. 注解的作用

注解(Annotation)是 Java 提供的一种元数据机制,用于为代码添加额外的信息。这些信息可以被编译器、开发工具或运行时框架读取,并用于以下场景:

  1. 标记代码
    标识特定代码的用途(如 @Override 标记方法重写)。
  2. 编译检查
    辅助编译器检测潜在错误(如 @Deprecated 提示已过时的方法)。
  3. 生成代码/文档
    通过注解处理器(APT)在编译时生成代码(如 Lombok 的 @Data)或生成文档(如 Javadoc)。
  4. 运行时处理
    结合反射机制,在运行时动态处理逻辑(如 Spring 的 @Autowired 依赖注入)。

2. 为什么需要注解

在没有注解时,开发者通常需要通过配置文件命名约定实现元数据配置,例如:

  • XML 配置:早期 Spring 框架依赖 XML 定义 Bean。
  • 命名规则:JUnit 3 要求测试方法以 test 开头。

注解的引入解决了以下问题:

  • 代码与配置分离:直接在代码中声明元数据,避免繁琐的配置文件。
  • 强类型检查:注解通过编译器确保正确性,减少人为错误。
  • 开发效率:简化重复性工作(如 Lombok 自动生成 Getter/Setter)。

3. 注解的原理

  1. 本质是接口
    注解通过 @interface 定义,继承自 java.lang.annotation.Annotation
  2. 元注解(Meta-Annotation)
    用于定义注解的行为,包括:
    • @Retention:指定注解的保留策略(源码、类文件、运行时)。
    • @Target:指定注解可应用的目标(类、方法、字段等)。
    • @Documented:注解是否包含在 Javadoc 中。
    • @Inherited:是否允许子类继承父类的注解。
  3. 注解处理器
    • 编译时处理:通过 APT 解析注解并生成代码(需继承 AbstractProcessor)。
    • 运行时处理:通过反射读取 RUNTIME 保留的注解(如 Class.getAnnotation())。

4. 内置注解示例

  1. 基本注解

    1
    2
    3
    
    @Override     // 检查方法是否重写父类方法
    @Deprecated  // 标记方法已过时
    @SuppressWarnings("unchecked") // 抑制编译器警告
    
  2. 元注解示例

    1
    2
    3
    4
    5
    6
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface MyAnnotation {
        String value() default "default";
        int priority() default 1;
    }
    

5. 自定义注解与使用

1. 定义注解

1
2
3
4
5
6
7
8
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME) // 注解在运行时保留
@Target(ElementType.METHOD)         // 只能用于方法
public @interface TestCase {
    String id();          // 必须提供的属性
    String desc() default "暂无描述"; // 可选属性
}

2. 使用注解

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class CalculatorTest {

    @TestCase(id = "TC001", desc = "测试加法")
    public void testAdd() {
        Calculator calc = new Calculator();
        assert calc.add(2, 3) == 5;
    }

    @TestCase(id = "TC002") // desc 使用默认值
    public void testSubtract() {
        Calculator calc = new Calculator();
        assert calc.subtract(5, 3) == 2;
    }
}

3. 通过反射处理注解

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class TestRunner {
    public static void main(String[] args) {
        Class<CalculatorTest> testClass = CalculatorTest.class;
        for (Method method : testClass.getDeclaredMethods()) {
            if (method.isAnnotationPresent(TestCase.class)) {
                TestCase testCase = method.getAnnotation(TestCase.class);
                System.out.println("执行测试用例: " + testCase.id());
                System.out.println("描述: " + testCase.desc());
                try {
                    method.invoke(testClass.newInstance());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

6. 注解的高级用法

1. 注解属性类型

  • 支持类型:基本类型、StringClass、枚举、注解、上述类型的数组。

  • 示例:

    1
    2
    3
    4
    5
    6
    7
    
    public @interface ComplexAnnotation {
        Class<?> targetClass();
        Status status() default Status.PENDING;
        String[] tags();
    }
    
    enum Status { PENDING, DONE }
    

2. 组合注解

通过元注解组合实现复杂逻辑:

1
2
3
4
5
@RestController         // Spring MVC 中的组合注解(包含 @Controller 和 @ResponseBody)
@RequestMapping("/api")
public class MyController {
    // ...
}

3. 编译时处理(APT)

自定义注解处理器生成代码:

1
2
3
4
5
6
7
8
9
@SupportedAnnotationTypes("com.example.MyAnnotation")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyAnnotationProcessor extends AbstractProcessor {
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        // 处理注解并生成代码
        return true;
    }
}

7. 注意事项

  1. 保留策略

    • RetentionPolicy.SOURCE:仅源码保留(如 @Override)。
    • RetentionPolicy.CLASS:类文件中保留(默认,通常用于字节码分析工具)。
    • RetentionPolicy.RUNTIME:运行时可通过反射读取(如 Spring 注解)。
  2. 性能影响
    频繁使用反射读取注解可能影响性能,建议缓存结果。

  3. 注解与继承
    默认不继承,需使用 @Inherited 元注解。

  4. 默认值限制
    注解属性必须有确定值(要么提供默认值,要么在使用时显式赋值)。

总结

场景 推荐方案
代码检查 使用 @Override@Deprecated
框架配置 Spring 的 @Component、JPA 的 @Entity
自动化测试 JUnit 的 @Test、自定义测试注解
代码生成 结合 APT 生成模板代码

核心价值:通过声明式编程简化开发,提升代码可维护性。合理使用注解,可大幅减少冗余代码,但需避免过度设计。