终于理解java注解了

发布时间:2024年01月24日

注解也被称为元数据,为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后的某个时刻非常方便地使用这些数据。

注解的语法比较简单,除了@符号的使用之外,它基本与java固有的语法一致。在java中,按照下面的格式定义一个注解

public @interface MyAnno {
    String name() default "";
}

元注解

在定义注解时,需要用到一些元注解,元注解专职负责注解其他的注解,用来说明注解的作用对象以及运行时期等。java目前内置了4种元注解。

注解说明
@Target表示该注解可以用于什么地方。可能的ElementType参数包括:
CONSTRUCTOR:构造器的声明
FIELD:域声明(包括enum实例)
LOCAL_VARIABLE:局部变量声明
METHOD:方法声明
PACKAGE:包声明
PARAMETER:参数声明
TYPE:类、接口(包括注解类型)或enum声明
@Retention表示需要在什么级别保存该注解信息。可选的RetentionPolicy参数包括:
SOURCE:注解将被编译器丢弃
CALSS:注解在class文件中可用,但会被VM丢弃
RUNTIME:VM将在运行期也保留注解,因此可以用过反射机制读取注解的信息。
@Documented将此注解包含在javadoc中。
@Inherited允许子类继承父类中的注解。

注解元素

注解元素可用的类型有如下几种:

  • 所有的基本类型(int、float、boolean等)

  • String

  • Class

  • enum

  • Annotation

  • 以上类型的数组

如果使用了其他类型,编译器就会报错。注意,也不允许使用任何包装类型,不过由于自动打包的存在,这不算是什么限制。注解也可以作为元素的类型,也就是说注解可以嵌套,这是一个很有用的技巧。

默认值限制

编译器对元素的默认值有些过分挑剔。

首先,元素不能有不确定的值。也就是说,元素必须要么具有默认值,要么在使用注解时提供元素的值。

其次,对于非基本类型的元素,无论是在源代码中声明时,或是在注解接口中定义默认值时,都不能以null作为其值。这个约束使得处理器很难表现一个元素的存在或缺失状态,因为在每个注解的声明中,所有的元素都存在,并且都具有相应的值。为了绕开这个约束,我们只能自己定义一些特殊的值,例如空字符串或负数,以此表述某个元素不存在。

一个自定义注解的栗子

下面是一个自定义注解的栗子。在这个例子中,自定义了@DBTable注解,用于表明要为有该注解的类生成建表语句,@Constraints、@SQLString、@SQLInteger注解表示数据库表字段相关的属性。四个注解的定义如下:

/**
 * 标记类是一张表
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBTable {
    /**
     * 表名
     */
    String name() default "";
}

/**
 * 字段属性
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Constraints {
    /**
     * 是否主键
     */
    boolean primaryKey() default false;

    /**
     * 是否允许为空
     */
    boolean allowNull() default true;

    /**
     * 是否唯一
     */
    boolean unique() default false;
}

/**
 * 数据库字符串类型
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLString {
    /**
     * 字段长度
     */
    int value() default 0;

    /**
     * 字段名称
     */
    String name() default "";

    Constraints constraints() default @Constraints;
}

/**
 * 数据库int类型
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLInteger {
    /**
     * 字段名称
     */
    String name() default "";

    Constraints constraints() default @Constraints;
}

注解有了,当注解被添加到具体的类的时候,还需要对应的处理注解的handle才行,否则,注解是不会生效的。生成sql语句的处理器如下:

/**
 * 注解处理器
 */
public class TableCreator {
    public static void main(String[] args) throws ClassNotFoundException {
        args = new String[]{"com.wgyang.demo.annotation.bean.Member"};
        for (String className : args) {
            String sql = createSql(className);
            System.out.println(sql);
        }
    }

    /**
     * 根据注解生成sql语句
     *
     * @param className 类的全限定名
     * @return sql建表语句
     * @throws ClassNotFoundException
     */
    private static String createSql(String className) throws ClassNotFoundException {
        Class<?> clazz = Class.forName(className);
        DBTable dbTable = clazz.getAnnotation(DBTable.class);
        if (dbTable == null) {
            return null;
        }
        String tableName = dbTable.name();
        if (tableName.isEmpty()) {
            tableName = clazz.getName().toUpperCase();
        }
        List<String> columns = new ArrayList<>();
        for (Field field : clazz.getDeclaredFields()) {
            String columnName = null;
            Annotation[] annos = field.getDeclaredAnnotations();
            if (annos.length < 1) {
                continue;
            }
            if (annos[0] instanceof SQLInteger) {
                SQLInteger sqlInteger = (SQLInteger) annos[0];
                if (sqlInteger.name().isEmpty()) {
                    columnName = field.getName().toUpperCase();
                } else {
                    columnName = sqlInteger.name();
                }
                columns.add(columnName + " INT" + getConstraints(sqlInteger.constraints()));
            }
            if (annos[0] instanceof SQLString) {
                SQLString sqlString = (SQLString) annos[0];
                if (sqlString.name().isEmpty()) {
                    columnName = field.getName().toUpperCase();
                } else {
                    columnName = sqlString.name();
                }
                columns.add(columnName + " VARCHAR(" + sqlString.value() + ")" + getConstraints(sqlString.constraints()));
            }
        }
        StringBuilder builder = new StringBuilder("CREATE TABLE " + tableName + "(");
        for (String column : columns) {
            builder.append("\n ").append(column).append(",");
        }

        return builder.substring(0, builder.length() - 1) + ");";
    }

    /**
     * 获取字段属性,并根据属性值拼接sql片段
     *
     * @param con 字段属性
     * @return sql语句片段
     */
    private static String getConstraints(Constraints con) {
        String constraints = "";
        if (!con.allowNull()) {
            constraints += " NOT NULL";
        }
        if (con.primaryKey()) {
            constraints += " PRIMARY KEY";
        }
        if (con.unique()) {
            constraints += " UNIQUE";
        }
        return constraints;
    }
}

对一个实体类添加自定义的注解,注解处理器便可以生成该实体对应的建表语句。

/**
 * 数据库表对象,该类添加了建表注解,注解处理器根据类上的注解生成建表语句
 */
@DBTable(name = "MEMBER")
public class Member {
    @SQLString(30)
    private String firstName;

    @SQLString(50)
    private String lastName;

    @SQLInteger
    private Integer age;

    @SQLString(value = 30, constraints = @Constraints(primaryKey = true))
    private String handle;
}

执行TableCreator类的main方法,输出结果如下:

CREATE TABLE MEMBER(
 FIRSTNAME VARCHAR(30),
 LASTNAME VARCHAR(50),
 AGE INT,
 HANDLE VARCHAR(30) PRIMARY KEY);
文章来源:https://blog.csdn.net/u013230391/article/details/135832734
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。