自定义注解

34 阅读3分钟

大学生小菜鸡学习记录,怕浪费佬们时间,可以现在划走

自定义注解相关基础知识

  1. 理解注解的本质

注解(Annotation)是一种为程序元素(类、方法、变量等)添加元数据的方式。它本身不直接影响逻辑,但可以被其他工具(如编译器、框架)读取并用于特定操作。

  1. 掌握自定义注解的创建

使用@interface关键字定义,其本质是一个自动继承了java.lang.annotation.Annotation的接口 定义时,你需要通过元注解来配置其行为,核心的元注解有两个:

  • @Target:指定注解可以应用在哪些地方,例如ElementType.METHOD表示注解只能用在方法上
  • @Retention:指定注解的生命周期
    • SOURCE:仅存在于源码,编译后丢弃(如 @Override
    • CLASS:存在于字节码文件,但运行时不可获取(默认策略)
    • RUNTIME运行时保留,可通过反射读取。绝大多数自定义注解(如Spring中的注解)都使用此策略
  1. 掌握注解的解析(反射)

定义了RUNTIME级别的注解后,必须通过Java的反射机制来读取它并执行相应逻辑。这是注解发挥作用的桥梁。

实战一:模拟简易ORM框架

这个案例会模拟像Hibernate那样,用注解将Java对象与数据库表进行映射

  1. 定义注解:创建两个注解。

    • @Table:标注类,表示对应的数据库表名
    • @Column:标注字段,表示对应的数据库列名

    java

    // 表注解
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Table {
        String value(); // 用于存储表名
    }
    // 列注解
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Column {
        String value(); // 用于存储列名
    }
    
  2. 应用注解:在一个实体类上使用定义好的注解。

    java

    @Table("user")
    public class User {
        @Column("user_name")
        private String name;
        @Column("user_age")
        private Integer age;
        // 省略getter/setter
    }
    
  3. 解析注解并生成SQL:编写一个工具方法,通过反射解析对象上的注解,拼接出SQL语句。

    java

    public static String generateInsertSQL(Object obj) throws Exception {
        Class<?> clazz = obj.getClass();
        // 1. 获取@Table注解值作为表名
        if (!clazz.isAnnotationPresent(Table.class)) {
            return null;
        }
        Table tableAnnotation = clazz.getAnnotation(Table.class);
        String tableName = tableAnnotation.value();
        StringBuilder sql = new StringBuilder("INSERT INTO " + tableName + " (");
        // 2. 遍历字段,获取@Column注解值作为列名,并通过getter获取值
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(Column.class)) {
                Column columnAnnotation = field.getAnnotation(Column.class);
                sql.append(columnAnnotation.value()).append(", ");
            }
        }
        // 3. 拼接完整的SQL字符串(此处需处理值的部分,代码已简化)
        sql.append(") VALUES (...);");
        return sql.toString();
    }
    

实战二:实现简单测试框架

这个案例模仿JUnit,通过注解来标记和运行测试方法,并自动捕获异常

  1. 定义注解:创建一个 @Check 注解,用于标记需要测试的方法。

    java

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Check {
    }
    
  2. 应用注解:在一个待测试的类中,用 @Check 标记要测试的方法。

    java

    public class Calculator {
        @Check
        public void add() { System.out.println("1 + 0 = " + (1 + 0)); }
        @Check
        public void div() { System.out.println("1 / 0 = " + (1 / 0)); } // 这里会抛出算术异常
    }
    
  3. 解析注解并运行测试:编写测试运行器,检查所有带 @Check 注解的方法,用反射调用它们并记录异常。

    java

    public class TestRunner {
        public static void main(String[] args) throws Exception {
            Calculator calc = new Calculator();
            Class<?> clazz = calc.getClass();
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                // 判断方法是否有@Check注解
                if (method.isAnnotationPresent(Check.class)) {
                    try {
                        method.invoke(calc); // 执行方法
                        System.out.println(method.getName() + ": 测试通过");
                    } catch (Exception e) {
                        System.out.println(method.getName() + ": 发生异常 - " + e.getCause().getMessage());
                    }
                }
            }
        }
    }