java注解

2019-04-01  本文已影响0人  Lucky胡

java注解起到给源代码加上标签的作用,加上和删除对源码没有影响。

外部工具访问标签(注解),然后做了处理。比如IDE识别到@Deprecated,则在代码方法上加上删除线。

识别标签的工具叫APT(Annotation Processing Tool)

元注解

用来给其他注解做注解的注解,一共有6个。
@Retention : 指定被此注解标注的注解保留时长

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}

有一个属性value,返回一个RetentionPolicy枚举,有3种类型:

public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     * 保留在源码中,编译器编译时删去
     */
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     * 编译器编译到class文件中,在运行时删去
     */
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     * 编译到class文件中,运行时也存在,可以用反射调用
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}

@Target : 用于标注该注解可以注解的程序元素

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation type
     * can be applied to.
     * @return an array of the kinds of elements an annotation type
     * can be applied to
     */
    ElementType[] value();
}

有一个value(),返回可以注解的程序元素ElementType数组,包括:

public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    // 类、接口(包括注解类型)、enum
    TYPE,

    /** Field declaration (includes enum constants) */
    // 属性(包括enum元素)
    FIELD,

    /** Method declaration */
    //方法
    METHOD,

    /** Formal parameter declaration */
    // 参数
    PARAMETER,

    /** Constructor declaration */
    // 构造方法
    CONSTRUCTOR,

    /** Local variable declaration */
    // 局部变量
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    // 注解类型
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE
}

@Documented : 将被标注的注解生成到javadoc中

@Inherited : 被其修饰的注解有继承能力。

@Inherited
public @interface InAnnotation{}

@InAnnotation
public class Base{}

//该子类即使没有写注解,也自动获得了父类的注解@InAnnotation
public class Son extends Base{}

@Repeatable : 被修饰的注解可以重复的注解某一个程序元素。

@ShuSheng(name="frank" age=20)
@ShuSheng(age=18)
public class AnnotationDemo{}

如何定义一个重复注解,首先需要定义一个容器,然后将其作为参数传入@Repeatable中

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.Type)
public @interface ShuShengs{
  ShuShen[] value();
}

@Repeatable(ShuShengs.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ShuShen{
  String name() default "ben"
  int age();
}

java基本注解

java内置注解有5个。
@Override
编译器检查被标记的方法,保证其重写了父类的方法

@Retention(RetentionPolity.SOURCE)
@Target(ElementType.METHOD)
public @interface Override{
}

@Deprecated
被注释的程序元素被废弃了,不要再使用了

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}

@SuppressWarnings
让编译器不要给出警告提示

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
  String[] value();
}

其中,需要传入一个String数组类型的参数,根据传入的数组来确定取消哪些类型的警告。

deprecation:使用了不赞成使用的类或方法时的警告;
unchecked:执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型;
fallthrough:当 Switch 程序块直接通往下一种情况而没有 Break 时的警告;
path:在类路径、源文件路径等中有不存在的路径时的警告;
serial:当在可序列化的类上缺少 serialVersionUID 定义时的警告;
finally:任何 finally 子句不能正常完成时的警告;
all:关于以上所有情况的警告。


@SafeVarargs
编译器在检测到你使用某些方法参数的使用有问题,会发出警告。使用该注解后,不让编译器警告。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface SafeVarargs {}

可以避免提示heap pollution

    @SafeVarargs
    private static void method(List<String>... strLists) {
        List[] array = strLists;
        List<Integer> tempList = new ArrayList<>();
        array[0] = tempList; //非法操作,会警告,heap pollution
        String s = strLists[0].get(0); //运行时会报错
    }

@FunctionalInterface
编译器检查是否是函数接口,即只有一个方法,则可以用lambda表达式。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}

自定义注解


@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface Annotation1 {
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Annotation2 {
    String name() default "hujun";
}

@Annotation2(name = "hj")
public class Member {
    @Annotation1
    public void test(){

    }
}

使用注解

注解不会自己起作用,需要APT,那APT怎么获取注解里的信息呢?用反射

        Class<?> cls = MemberSon.class;

        //获取该类上的所有注解
        Annotation[] annotations = cls.getAnnotations();
        System.out.println( "getAnnotations: " + Arrays.asList(annotations).toString());

        //检查是否有某注解
        boolean present = cls.isAnnotationPresent(Annotation1.class);
        System.out.println("getAnnotation: Annotation1 present :" + present);

        //获取重复注解
        Annotation1[] annotation1 = cls.getAnnotationsByType(Annotation1.class);

        Method[] methods = Member.class.getMethods();
        for (Method method: methods) {
            System.out.println("method : "+method.getName()+",getAnnotation:"+Arrays.asList(method.getAnnotations()).toString());
        }

构建自己的注解APT

利用APT来自动生成SQL的语句。
首先定义数据库相关的注解。

/**
 * 注解表
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface DBTable {
    String name() default "";
}


/**
 * 约束注解
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Constraints {
    //是否作为主键
    boolean primaryKey() default false;

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

    //是否允许为空
    boolean allowNull() default false;
}



/**
 * 注解String类型的字段
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLString {
    //对应数据库表的列名
    String name() default "";
    //列类型分配的长度,如varchar(30)的30
    int value() default 0;
    Constraints constraint() default @Constraints;
}


/**
 * 注解Integer类型的字段
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SQLInteger {
    //对应数据库表的列名
    String name() default "";

    //列类型分配的长度,例如varchar(30)的30
    int value() default 0;

    //约束条件
    Constraints constraint() default @Constraints;
}

然后用自定义的数据库注解来注解要生成的数据库类。

@DBTable(name = "PERSON")
public class Person {
    @SQLString(name = "ID",value = 50,constraint = @Constraints(primaryKey = true,unique = true))
    private String id;

    @SQLString(name = "NAME",value = 30,constraint = @Constraints(unique = true))
    private String name;

    @SQLInteger(name = "AGE")
    private int age;

    @SQLString(name = "INFO",value = 150,constraint = @Constraints(allowNull = true))
    private String info;
}

其中使用了嵌套注解@Constraint,可以作为注解里的参数。

public class SQLAPT {
    public static String createTableSql(String className) throws ClassNotFoundException {
        Class<?> cl = Class.forName(className);
        DBTable dbTable = cl.getAnnotation(DBTable.class);
        //如果没有表注解,直接返回
        if(dbTable == null) {
            System.out.println(
                    "No DBTable annotations in class " + className);
            return null;
        }
        String tableName = dbTable.name();
        // If the name is empty, use the Class name:
        if(tableName.length() < 1) {
            tableName = cl.getName().toUpperCase();
        }
        List<String> columnDefs = new ArrayList<String>();
        //通过Class类API获取到所有成员字段
        for(Field field : cl.getDeclaredFields()) {
            String columnName = null;
            //获取字段上的注解
            Annotation[] anns = field.getDeclaredAnnotations();
            if(anns.length < 1) {
                continue; // Not a db table column
            }

            //判断注解类型
            if(anns[0] instanceof SQLInteger) {
                SQLInteger sInt = (SQLInteger) anns[0];
                //获取字段对应列名称,如果没有就是使用字段名称替代
                if(sInt.name().length() < 1) {
                    columnName = field.getName().toUpperCase();
                } else {
                    columnName = sInt.name();
                }
                //构建语句
                columnDefs.add(columnName + " INT" +
                        getConstraints(sInt.constraint()));
            }
            //判断String类型
            if(anns[0] instanceof SQLString) {
                SQLString sString = (SQLString) anns[0];
                // Use field name if name not specified.
                if(sString.name().length() < 1) {
                    columnName = field.getName().toUpperCase();
                } else {
                    columnName = sString.name();
                }
                columnDefs.add(columnName + " VARCHAR(" +
                        sString.value() + ")" +
                        getConstraints(sString.constraint()));
            }
        }
        //数据库表构建语句
        StringBuilder createCommand = new StringBuilder(
                "CREATE TABLE " + tableName + "(");
        for(String columnDef : columnDefs) {
            createCommand.append("\n    " + columnDef + ",");
        }

        // Remove trailing comma
        String tableCreate = createCommand.substring(
                0, createCommand.length() - 1) + ");";
        return tableCreate;
    }

    /**
     * 判断该字段是否有其他约束
     * @param con
     * @return
     */
    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;
    }
}
上一篇下一篇

猜你喜欢

热点阅读