Java 注解实战

Java 中提供了注解的功能。涉及到的包有:java.lang.annotationjava.lang.reflect
也就是说,实际上包含两点:注解和反射。

注解

  • q:为什么需要反射?

因为仅仅自定义注解的话,几乎没有任何作用,除非是基于第三方框架。而不依赖任何第三方框架的话,就必须使用到反射来解析自定义的注解。(似乎也有其他的方式来解析)

这里就使用反射的方式来解析。

关于自定义注解本身,其实内容很少。

比如,下面有自定义一个注解类:

@Target(value = {ElementType.TYPE, ElementType.METHOD, ElementType.LOCAL_VARIABLE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Type {

    String params() default "intelliJ";
    
    int pos();

    char[] value();
}

简单说明一下:

  • @Target 指定该注解使用的位置。比如上面的注解表示,该注解可以用于类,方法,局部变量。
  • @Retention 指定该注解的生命周期。比如上面的注解表示,该注解在运行时依然存在。*特别注意,如果自定义注解需要被反射解析,必须要指定生命周期为RetentionPolicy.RUNTIME
  • 该注解里面指定了三个属性:parmas , pos , value 这些属性必须被指定,在使用注解的时候。除非定义的时候,对应的属性后面跟上了default xxx

然后看一下对应的注解使用类:

@Type(params = "idea", pos = 1, value = {'a', 'b', 'm'})
public class TypeObject {
    @Type(pos = 3, value = 'd')
    private int add(int a, int b) {
        @Type(params = "potato", value = 'f', pos = 12)
        int c = a + b;
        return c;
    }
}

使用注解比较简单,这里给出使用的案例,只是对照一下上面的说明。


以上,自定义注解的内容其实就差不多结束了。

但是,到此为止,并没有发现自定义注解有什么意义。
而且,到目前为止,确实没有任何意义。

以上注解,到目前为止确实没有对TypeObject产生任何影响。

注解解析

如果只是像上面这样,自定义了注解,并使用了注解。并没有什么实际意义。而自定义的解析,就可以解析被注解的类的注解内容。这样就能产生相关效果。

下面来弄一个简单的orm小工具 ,用到以上的自定义注解,以及自定义注解的解析

大致实现效果,就是有一个POJO类,然后给它自定义一些注解,最后解析这些注解,生成相应的 sql 语句。(实际的orm框架,肯定还会去执行这些sql语句,但是这里只是展示一些自定义注解的解析。就不去执行了)

  1. POJO类 --> User.java
@BindTable(value = "tb_user")
public class User {

    public static final int GENDER_SECRET = 0;
    public static final int GENDER_MAIL = 1;
    public static final int GENDER_FEMAIL = 2;

    @BindField(type = "varchar(50)", notNull = true, columnName = "_name")
    private String name;
    @BindField(type = "int(3)", columnName = "_age")
    private int age;
    @BindField(type = "int(1)", notNull = true, columnName = "_gender")
    private int gender;

    @BindField(type = "int(1)", primaryKey = true, columnName = "_uid")
    private long uid;
// get/set 去掉了 ... 
  1. 注解类:BindTable.java , BindField.java
@Target(value = ElementType.TYPE)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface BindTable {

    /**
     * table name
     *
     * @return table name
     */
    String value();

    String charset() default "utf8";
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BindField {

    // `_uid` int(32) NOT NULL AUTO_INCREMENT,

    String columnName() default "";

    /**
     * 字段类型:varchar , int(25)
     *
     * @return field type
     */
    String type();

    boolean notNull() default false;

    boolean primaryKey() default false;

    boolean unique() default false;

    boolean autoIncrement() default false;
}

  1. 注解解析类:TbUserParser.java
public class TbUserParser {

    private TbUserParser() {
    }

    private static TableInfo parseClass() {

        BindTable annotation = (BindTable) ((Class) User.class).getAnnotation(BindTable.class);
//        System.out.println(annotation);
        String charset = annotation.charset();
        String tbName = annotation.value();
        return new TableInfo(charset, tbName);

    }


    private static List parserField() {

        Field[] fields = User.class.getDeclaredFields();
        ArrayList infoSet = new ArrayList<>();

        for (Field f : fields) {

            // 没有添加该注解的字段就会返回 null
            BindField bindF = f.getAnnotation(BindField.class);

            // System.out.println("bindF==" + bindF);
            if (Objects.nonNull(bindF)) {
                String columnName = bindF.columnName();
                String type = bindF.type();
                boolean unique = bindF.unique();
                boolean autoIncrement = bindF.autoIncrement();
                boolean notNull = bindF.notNull();
                boolean primaryKey = bindF.primaryKey();
                FieldInfo info = new FieldInfo(columnName, type, notNull,
                        autoIncrement, unique, primaryKey);
                infoSet.add(info);
            }
        }
        return infoSet;
    }


    public static String createTable() {
        TableInfo tableInfo = parseClass();
        List fieldInfoSet = parserField();
        String head = String.format(Locale.getDefault(),
                "CREATE TABLE IF NOT EXISTS %s(", tableInfo.tbName);
        StringBuilder item = new StringBuilder();
        for (FieldInfo fi : fieldInfoSet) {
            // `chat_id` INT UNSIGNED AUTO_INCREMENT,
            item.append(String.format(Locale.getDefault(), "`%s` ", fi.columnName))
                    .append(String.format(Locale.getDefault(), "%s ", fi.type))
                    .append(fi.primaryKey ? "PRIMARY KEY " : "")
                    .append(fi.notNull ? "NOT NULL " : "")
                    .append(fi.unique ? "UNIQUE " : "")
                    .append(fi.autoIncrement ? "AUTO_INCREMENT," : ",");
        }
        if (item.charAt(item.length() - 1) == ',') {
            item.deleteCharAt(item.length() - 1);
        }
        String tail = String.format(Locale.getDefault(), ") CHARSET=%s;", tableInfo.charset);
        return head + item + tail;
    }


    static class TableInfo {
        String charset;
        String tbName;

        TableInfo(String charset, String tbName) {
            this.charset = charset;
            this.tbName = tbName;
        }
    }

    static class FieldInfo {
        String columnName;
        String type;
        boolean notNull;
        boolean autoIncrement;
        boolean unique;
        boolean primaryKey;

        FieldInfo(String columnName, String type, boolean notNull,
                  boolean autoIncrement, boolean unique, boolean primaryKey) {
            this.columnName = columnName;
            this.type = type;
            this.notNull = notNull;
            this.autoIncrement = autoIncrement;
            this.unique = unique;
            this.primaryKey = primaryKey;
        }
    }
}

最后是调用:

public static void main(String[] args) {

    String table = TbUserParser.createTable();
    System.out.println("table==\n" + table);
}

输出如下

table==
CREATE TABLE IF NOT EXISTS tb_user(`_name` varchar(50) NOT NULL ,`_age` int(3) ,`_gender` int(1) NOT NULL ,`_uid` int(1) PRIMARY KEY ) CHARSET=utf8;

这样,就完成了对自定义注解的解析。

简单说明一下自定义注解的解析,其实核心代码很少,就是对反射的使用。

// 对类上面注解的信息获取
BindTable annotation = (BindTable) ((Class) User.class).getAnnotation(BindTable.class);
//        System.out.println(annotation);
String charset = annotation.charset();
String tbName = annotation.value();

// 对字段上面的注解信息获取:

Field[] fields = User.class.getDeclaredFields();
        ArrayList infoSet = new ArrayList<>();

        for (Field f : fields) {

            // 没有添加该注解的字段就会返回 null
            @Nullable
            BindField bindF = f.getAnnotation(BindField.class);

            // System.out.println("bindF==" + bindF);
            if (Objects.nonNull(bindF)) {
                String columnName = bindF.columnName();
                String type = bindF.type();
                boolean unique = bindF.unique();
                boolean autoIncrement = bindF.autoIncrement();
                boolean notNull = bindF.notNull();
                boolean primaryKey = bindF.primaryKey();
                FieldInfo info = new FieldInfo(columnName, type, notNull,
                        autoIncrement, unique, primaryKey);
                
            }
        }

对于方法上面的注解解析,这里没有做,应该也是类似的调用。

以上。戳我,给我 star , 看运行效果 star 2333

特别感谢:Java课程 Java300集教程 SXT

ps 真不是帮打广告。我对自定义注解的理解,全是基于此讲解。

你可能感兴趣的:(学习笔记,Java)