• 注解


    一、注解的定义

    1.注解(Annotation),也叫元数据。它是jdk1.5后引入的一个新的特性。与类,接口,枚举是同一个层次。可以声明在类、字段、方法、局部变量、方法参数等的前面。注解也属于一种类型,有自己的语法

    1 @Target(ElementType.METHOD)
    2 @Retention(RetentionPolicy.RUNTIME)
    3 public @interface TestAnnotation {
    4 
    5 }

    如上所示,声明一个注解很简单,使用@interface声明TestAnnotation注解,并且使用两个java内置元注解(标识其他注解的注解)进行修饰。

    二、java中常见的注解

    1.元注解

    元注解专职负责注解其他注解,主要有下面四种:

    • @Target,主要用来约束注解可以使用的地方(如方法、类或字段)
    • @Retention,主要用来约束注解的生命周期,分别有三个值,源码级别(source),类文件级别(class)或者运行时级别(runtime),主要区别:RetentionPolicy.SOURCE:注解将被编译器丢弃(该类型的注解信息只会保留在源码里,源码经过编译后,注解信息会被丢弃,不会保留在编译好的class文件里)RetentionPolicy.CLASS:注解在class文件中可用,但会被VM丢弃(该类型的注解信息会保留在源码里和class文件里,在执行的时候,不会加载到虚拟机中),请注意,当注解未定义Retention值时,默认值是CLASS,如Java内置注解,@Override、@Deprecated、@SuppressWarnning等。RetentionPolicy.RUNTIME:注解信息将在运行期(JVM)也保留,因此可以通过反射机制读取注解的信息(源码、class文件和执行的时候都有注解的信息),如SpringMvc中的@Controller、@Autowired、@RequestMapping等。
    • @Document,将此注解包含在Jacadoc中
    • @Inherited,允许子类继承父类的注解

    2.标准注解

    • @Deprecated,Deprecated是弃用的意思,顾名思义这个注解用来标识过时的元素,例如过时的类,方法或者成员变量。
    • @Override,这可能是我们最熟悉的注解之一了,表示被注解的方法是复写了当前类父类的方法。
     1 public class Demo{
     2     @Deprecated
     3     public void test(){
     4         System.out.println("我已经被弃用");
     5     }
     6     @Override
     7     public boolean equals(Object obj) {
     8         return super.equals(obj);
     9     }
    10 }        
    • @SuppressWarnings,主要用来忽略各种警告用的。用于有选择的关闭编译器对类、方法、成员变量、变量初始化的警告,使用如下:
    public class Demo2 {
      @SuppressWarnings("deprecation")
        public void test2(){
            Demo demo = new Demo();
            demo.test();//当使用@SuppressWarnings("deprecation")后,警告消失
        }
    
    }    

    上例只是SuppressWarnings的一种,以上三种注解的详细使用说明可以参考源码。例如SuppressWarnnings注解的源码如下:

     1 @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
     2 @Retention(RetentionPolicy.SOURCE)
     3 public @interface SuppressWarnings {
     4 String[] value();
     5 /*
     6 deprecation:使用了不赞成使用的类或方法时的警告;
     7 unchecked:执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型; 
     8 fallthrough:当Switch 程序块直接通往下一种情况而没有 Break 时的警告;
     9 path:在类路径、源文件路径等中有不存在的路径时的警告; 
    10 serial:当在可序列化的类上缺少 serialVersionUID 定义时的警告; 
    11 finally:任何 finally 子句不能正常完成时的警告; 
    12 all:关于以上所有情况的警告。
    13 */
    14 }

    3.注解元素

    1).注解元素可用的类型如下所示:

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

    • String
    • Class
    • enum
    • Annotation
    • 以上类型的数组

    2).注解元素的默认值限制

    编译器对注解元素的默认值有些过分的挑剔,元素不能有不确定的值,即元素必须要有默认值,要么在使用注解的时候提供元素的值。其次对于非基本类型的元素,无论是源码中声明时,或是注解接口中定义默认值的时候,不能以null作为其默认值。在注解的声明中,所有的元素都存在,并且具有其值,可以赋予空字符串或者负数,如下:

    1 @Target(ElementType.METHOD)
    2 @Retention(RetentionPolicy.RUNTIME)
    3 public @interface SimplDemo {
    4     public int id() default -1;
    5     public String description() default "";//元素的定义类似接口中方法的定义
    6     
    7 }

    注意:不能使用关键字extends来继承某个@interface.

    二、自定义注解的使用

    1.自定义注解的简单使用

    自定义一个注解TestAnnotation,主要用在方法上,首先定义一个注解,如下所示:

    1 @Target(ElementType.METHOD)
    2 @Retention(RetentionPolicy.RUNTIME)
    3 public @interface TestAnnotation {
    4     String name() ;
    5     String value();
    6 }

     定义注解的使用如下:

     1 public class Demo {
     2     @TestAnnotation(name = "test", value = "zz")
     3     public void test() {
     4         System.out.println("被注解方法执行完毕");
     5     }
     6 
     7     public void test2() {
     8 
     9     }
    10 
    11     public static void main(String[] args) {
    12         Demo demo = new Demo();
    13         Class<?> clz = demo.getClass();
    14         Method[] methods = clz.getDeclaredMethods();
    15         for (Method method : methods) {
    16             // 遍历Demo类定义的所有方法,如果方法被TestAnnotation注解,则执行方法,并获取注解的相关属性
    17             if (method.isAnnotationPresent(TestAnnotation.class)) {
    18                 method.setAccessible(true);
    19                 try {
    20                     method.invoke(demo, null);
    21                 } catch (Exception e) {
    22                     e.printStackTrace();
    23                 }
    24 
    25                 Annotation[] annotations = method.getAnnotations();
    26 
    27                 for (Annotation annotation : annotations) {
    28                     System.out.println(annotation);
    29                     if (annotation instanceof TestAnnotation) {
    30                         TestAnnotation testAnnotation = (TestAnnotation) annotation;
    31                         System.out.println(testAnnotation.name() + "--"
    32                                 + testAnnotation.value());
    33                     }
    34                 }
    35 
    36             }
    37         }
    38 
    39     }
    40 
    41 }

    结果输出:

    被注解方法执行完毕
    @com.demo.TestAnnotation(name=test, value=zz)
    test--zz

    注意:当TestAnnotation注解的保留策略是RetentionPolicy.CLASS和RetentionPolicy.SOURCE时,通过反射是获取不到注解信息的。

    通常对注解的使用,都是结合反射一起,如上通过反射和注解可以决定是都执行Demo对象的某个方法。上述例子也只是对注解的一个简单的使用,注解带来的好处和用法太多。

    主要有下面三方面:
    -提供信息给编译器:编译器可以利用注解来探测错误和警告信息
    -编译阶段时的处理:软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。
    -运行时的处理: 某些注解可以在程序运行的时候接受代码的提取

    三、注解与反射

    有句话叫"纸上得来终觉浅,绝知此事要躬行",可能对注解还是一知半解,很多东西都是从不懂到懂,需要一个过程,可能这个过程是痛苦的,不过只要坚持,迟早会懂。等到了那一天反而会觉得很简单。通过实际的代码去理解理论知识,是最好的途径,特别是当在工作中使用了后就更有体会。下面这个例子是java编程思想中的一个经典的例子,能够帮助很好的理解注解和反射的关系。

     1 @Target(ElementType.TYPE)//用于注解类
     2 @Retention(RetentionPolicy.RUNTIME)
     3 public @interface DBTable {
     4     public String name() default "";
     5 }
     6 
     7 --------------------------------------------------------------
     8 @Target(ElementType.FIELD)
     9 @Retention(RetentionPolicy.RUNTIME)
    10 public @interface Constraints {
    11     boolean primaryKey() default false;
    12     boolean allowNull() default true;
    13     boolean unique() default false;
    14 }
    15 
    16 ---------------------------------------------------------------
    17 @Target(ElementType.FIELD)
    18 @Retention(RetentionPolicy.RUNTIME)
    19 public @interface SQLString {
    20     int value() default 0;
    21     String name() default "";
    22     Constraints constraints() default @Constraints;
    23 
    24 }
    25 
    26 ----------------------------------------------------------------
    27 @Target(ElementType.FIELD)
    28 @Retention(RetentionPolicy.RUNTIME)
    29 public @interface SQLInteger {
    30     String name() default "";
    31     Constraints constraints() default @Constraints;
    32 }

    应用上述自定义注解的类

     1 @DBTable(name="MEMBER")
     2 public class Member {
     3     @SQLString(30)
     4     String firstName;
     5     @SQLString(50)
     6     String lastName;
     7     @SQLInteger
     8     Integer age;
     9     @SQLString(value=30,constraints=@Constraints(primaryKey=true,allowNull=false,unique=true))
    10     String handle;
    11     static int memberCount;
    12     
    13     public String getHandle(){
    14         return handle;
    15     }
    16     public String getFirstName(){
    17         return firstName;
    18     }
    19     public String getLastName(){
    20         return lastName;
    21     }
    22     public String toString(){
    23         return handle;
    24     }
    25     public Integer getAge(){
    26         return age;
    27     }
    28 }

    实现处理器,利用反射和注解实现自动生产创建数据库表的SQL语句

     1 /**
     2  * 注解处理器
     3  *    
     4  */
     5 public class TableCreator {
     6     public static void main(String[] args)throws Exception {
     7         Class<?> clz = Class.forName("com.sun.lp.demo.Member");
     8         
     9         DBTable dbTable= clz.getAnnotation(DBTable.class);//获取DBTable注解
    10         if (dbTable==null) {
    11             System.out.println("no DBTable annotation in class Member");
    12             System.exit(0);
    13         }
    14         
    15         String tableName = dbTable.name();
    16         if(tableName.length()<1){
    17             tableName=clz.getName().toUpperCase();
    18         }
    19         List<String> list = new ArrayList<String>();
    20         String tableCreate =null;
    21         for(Field field:clz.getDeclaredFields()){
    22             String colName=null;
    23             Annotation [] annotations = field.getAnnotations();
    24             if(annotations.length<1)
    25                 continue;
    26             if(annotations[0] instanceof SQLInteger){
    27                 SQLInteger sInt = (SQLInteger) annotations[0];
    28                 if(sInt.name().length()<1)
    29                     colName = field.getName().toUpperCase();
    30                 else 
    31                     colName = sInt.name();
    32                 list.add(colName+" INT"+getConstraints(sInt.constraints()));
    33             }
    34             if(annotations[0] instanceof SQLString){
    35                 SQLString sString = (SQLString) annotations[0];
    36                 if(sString.name().length()<1)
    37                     colName = field.getName().toUpperCase();
    38                 else 
    39                     colName = sString.name();
    40                 list.add(colName+" VARCHAR("+sString.value()+")"+getConstraints(sString.constraints()));
    41             }
    42             
    43         }
    44         StringBuilder createCommand = new StringBuilder("CREATE TABLE "+tableName+"(");
    45         for(String colDef : list)
    46             createCommand.append("
       "+colDef+",");
    47         tableCreate = createCommand.substring(0,(createCommand.length()-1))+");";
    48         System.out.println(tableCreate);
    49     }
    50     
    51     private static String getConstraints(Constraints con){
    52         String constraints = "";
    53         if(!con.allowNull())
    54             constraints+=" NOT NULL";
    55         if(con.primaryKey())
    56             constraints+=" PRIMARY KEY";
    57         if(con.unique())
    58             constraints+=" UNIQUE";
    59         
    60         return constraints;
    61         
    62     }
    63 
    64 }

    结果输出:

    CREATE TABLE MEMBER(
       FIRSTNAME VARCHAR(30),
       LASTNAME VARCHAR(50),
       AGE INT,
       HANDLE VARCHAR(30) NOT NULL PRIMARY KEY UNIQUE);
  • 相关阅读:
    redis-原理-数据结构-链表(二)
    redis-原理-数据结构-SDS(一)
    springMVC源码阅读-解决body不能重复读取问题(十二)
    spring-security使用-安全防护HttpFirewall(七)
    Redis-6.2.1 主从和哨兵模式配置
    Navicat 连接Mysql 8.0以上版本报错1251
    docker logs命令查看容器的日志
    Nginx 反向代理
    docker swarm 删除节点 (解散集群)
    Docker Swarm 命令学习
  • 原文地址:https://www.cnblogs.com/liupiao/p/9273664.html
Copyright © 2020-2023  润新知