zoukankan      html  css  js  c++  java
  • Java 注解(Annotation)概念及实战

    摘要:

      java注解:java 提供了一种源程序中的 元素 关联任何信息和任何元数据的途径和方法。

      学习注解的目的:

    • 能够读懂别人写的代码,特别是框架相关的代码
    • 让编程更加简洁,代码更加清晰
    • 让别人高看一眼,特别是会使用自定义注解

    目录:

    1. java中的常见注解

    2. 注解分类

    3. 自定义注解

    4. 注解应用实践

    1. java中的常见注解

    1) JDK 自带的注解
      @Override @Deprecated @SuppressWarnings
    2) 常见的第三方注解
      Spring @Autowired @Service @Repository
      MyBatis @InsertProvider @UpdateProvider @Options

    2. 注解分类

    1) 按照运行机制分:源码注解、编译时注解(如jDK 自带的三个注解)、运行时注解(如@Autowired)
    2) 按照来源来分:JDK的注解、第三方的注解、用户自定义的注解

    3. 自定义注解

    1) 自定义注解的语法要求

     1 import java.lang.annotation.Documented;
     2 import java.lang.annotation.ElementType;
     3 import java.lang.annotation.Inherited;
     4 import java.lang.annotation.Retention;
     5 import java.lang.annotation.RetentionPolicy;
     6 import java.lang.annotation.Target;
     7 
     8 /*元注解*/
     9 
    10 /*注解作用域的列表
    11 CONSTRUCTOR     构造方法声明
    12 FIELD           字段声明
    13 LOCAL_VARIABLE  局部变量声明
    14 METHOD          方法声明
    15 PACKAGE         包声明
    16 PARAMETER       参数声明
    17 TYPE       类,接口*/
    18 @Target({ ElementType.METHOD, ElementType.TYPE })
    19 
    20 /*
    21  * 注解的生命周期 
    22  * SOURCE 自在源码中显示,编译时会丢弃 
    23  * CLASS 编译时会记录到class中,运行时忽略
    24  * RUNTIME运行时存在,可以通过反射读取
    25  */
    26 @Retention(RetentionPolicy.RUNTIME)
    27 
    28 /* 标识性元注解,允许子类继承父类的注解,只在类上有效,接口和方法无效 */
    29 @Inherited
    30 
    31 /* 标识性元注解,生成 javadoc 时会包含注解 */
    32 @Documented
    33 
    34 /* 使用 @interface 关键字定义注解 */
    35 public @interface Description {
    36 
    37     /**
    38      * 成员类型是受限的,合法的类型包括原始类型及String,Class,Annotation,Enumeration
    39      * 如果注解只有一个成员,则成员名必须为 value(),在使用时可以忽略成员名和赋值号 
    40      * 注解类可以没有成员,没有成员的注解类成为标识注解
    41      */
    42 
    43      String deco(); //成员以无参无类型方式声明
    44       
    45      String author();
    46      
    47      int age() default 18; //可以用default为成员制定一个默认的值 
    48 }

    2) 元注解(注解的注解)

      @Target

      @Retention

      @Inherited

      @Documented

      元注解,相当于是注解的注解,用来对注解的作用域、作用时期等进行解释声明(具体内容看上面的代码)。

    3) 自定义注解的使用

      @<注解名>(<成员名1>=<成员值1>,<成员名2>=<注解值2>,...)
    例:
      @Description(desc="I am eyeColor", author="Mooc Boy", age=18)
      public String eyeColor() {
       return "red";
      }

    特殊情况:

      标识注解:
      @<注解名>
      只有一个成员的注解:
      @<注解名>(<成员值>)

    4) 解析注解

      通过反射获取类、函数或成员上的运行时注解信息,从而实现动态控制程序运行的逻辑。

      在本例中,定义一个接口 Person,一个实现类 Child,一个自定义的解析类 Description 以及一个解析注解的测试类ParseAnn,通过此例可以看到解析注解的作用。

    Person.java

    1 public interface Person {
    2     public String name();
    3     
    4     public int age();
    5     
    6     @Deprecated  //表明该方法已经过时,可以使用,但不推荐使用
    7     public void sing();
    8 }

    Child.java

     1 @Description("I am class annotation")
     2 public class Child implements Person {
     3 
     4     @Override
     5     @Description("I am method annotation")
     6     public String name() {
     7         return null;
     8     }
     9 
    10     @Override
    11     public int age() {
    12         return 0;
    13     }
    14 
    15     @Override
    16     public void sing() {
    17         
    18     }
    19 }

    Description.java

     1 import java.lang.annotation.Documented;
     2 import java.lang.annotation.ElementType;
     3 import java.lang.annotation.Inherited;
     4 import java.lang.annotation.Retention;
     5 import java.lang.annotation.RetentionPolicy;
     6 import java.lang.annotation.Target;
     7 
     8 @Target({ ElementType.METHOD, ElementType.TYPE })
     9 @Retention(RetentionPolicy.RUNTIME)
    10 @Inherited
    11 @Documented
    12 
    13 /* 使用 @interface 关键字定义注解 */
    14 public @interface Description {
    15     String value();
    16 }

    ParseAnn.java

     1 import java.lang.annotation.Annotation;
     2 import java.lang.reflect.Method;
     3 
     4 public class ParseAnn {
     5 
     6     public static void main(String[] args) {
     7         // 1.使用类加载器加载类
     8         try {
     9             Class c = Class.forName("com.ann.test.Child");
    10             // 2.找到类上面的注解
    11             boolean isExist = c.isAnnotationPresent(Description.class);
    12             if (isExist) {
    13                 // 3.拿到注解实例
    14                 Description d = (Description) c.getAnnotation(Description.class);
    15                 System.out.println(d.value());
    16             }
    17 
    18             // 4.找到方法上的注解
    19             Method[] ms = c.getMethods();
    20             for (Method m : ms) {
    21                 boolean isMExist = m.isAnnotationPresent(Description.class);
    22                 if (isMExist) {
    23                     Description d = (Description) m.getAnnotation(Description.class);
    24                     System.out.println(d.value());
    25                 }
    26             }
    27 
    28             // 另外一种解析方法
    29             for (Method m : ms) {
    30                 Annotation[] as = m.getAnnotations();
    31                 for (Annotation a : as) {
    32                     if (a instanceof Description) {
    33                         Description d = (Description) a;
    34                         System.out.println(d.value());
    35                     }
    36                 }
    37             }
    38         } catch (ClassNotFoundException e) {
    39             e.printStackTrace();
    40         }
    41     }
    42 }

    输出结果:

    I am class annotation
    I am method annotation
    I am method annotation

    4. 注解应用实践

    需求:
      1.有一个用户表,字段包括用户ID,用户名,昵称,年龄,性别,所在城市,邮箱,手机号。
      2.方便的对每个字段或字段的组合条件进行检索,并打印出SQL。
      3.使用方式要足够简单,见代码示例。

      首先考虑代码如何与数据库进行一个映射,比如程序如何知道数据库的表名、字段名,这就可以用到注解,利用注解解析实现。

          每一个字段都有一个 get 方法,通过反射调用 get 方法来取得字段的值 getMethod.invoke()。

      对于注解,特别是运行时注解,相当于在类、方法、字段等内容上套上一层“外衣”,可以根据“外衣”分类,找到相关内容,从而进行操作。
      

      java类:Filter.java 定义了字段内容,Test.java 测试类。

      解析类:Table.java 表解析,Column.java 字段解析。

    Table.java

    1 @Target({ ElementType.TYPE })
    2 @Retention(RetentionPolicy.RUNTIME)
    3 public @interface Table {    
    4     String value();
    5 }

    Column.java

    1 @Target({ ElementType.FIELD })
    2 @Retention(RetentionPolicy.RUNTIME)
    3 public @interface Column {    
    4     String value();
    5 }

    Filter.java

     1 @Table("user")
     2 public class Filter {
     3     
     4     @Column("id")
     5     private int id;
     6     
     7     @Column("userName")
     8     private String userName;
     9     
    10     @Column("nickName")
    11     private String nickName;
    12     
    13     @Column("age")
    14     private int age;
    15     
    16     @Column("city")
    17     private String city;
    18     
    19     @Column("email")
    20     private String email;
    21     
    22     @Column("mobile")
    23     private String mobile;
    24 
    25     public int getId() {
    26         return id;
    27     }
    28 
    29     public void setId(int id) {
    30         this.id = id;
    31     }
    32 
    33     public String getUserName() {
    34         return userName;
    35     }
    36     /*自动生成的get set 方法*/
    37 }

    Test.java

     1 public class Test {
     2 
     3     public static void main(String[] args) {
     4         Filter f1 = new Filter();
     5         f1.setId(10);// 表示查询ID为10的用户
     6 
     7         Filter f2 = new Filter();
     8         f2.setUserName("lucy");// 查询用户名为lucy的用户
     9         f2.setAge(18);
    10 
    11         Filter f3 = new Filter();
    12         f3.setEmail("liu@sina.com,zh@163.com,123@qq.com");// 查询邮箱为其中任意一个的用户
    13 
    14         String sql1 = query(f1);
    15         String sql2 = query(f2);
    16         String sql3 = query(f3);
    17 
    18         System.out.println(sql1);
    19         System.out.println(sql2);
    20         System.out.println(sql3);
    21     }
    22 
    23     private static String query(Filter f) {
    24         StringBuilder sb = new StringBuilder();
    25         // 1.获取到class
    26         Class c = f.getClass();
    27         // 2.获取到Table的名字
    28         boolean exists = c.isAnnotationPresent(Table.class);
    29         if (!exists) {
    30             return null;
    31         }
    32         Table t = (Table) c.getAnnotation(Table.class);
    33         String tableName = t.value();
    34         sb.append("select * from ").append(tableName).append(" where 1=1");
    35         // 3.遍历所有的字段
    36         Field[] fArray = c.getDeclaredFields();
    37         for (Field field : fArray) {
    38             // 4.处理每个字段对应的sql
    39             // 4.1 拿到字段名
    40             boolean fExists = field.isAnnotationPresent(Column.class);
    41             if (!fExists) {
    42                 continue; // 如果不是数据库的字段,不做处理
    43             }
    44             Column column = field.getAnnotation(Column.class);
    45             String columnName = column.value();
    46             // 4.2 拿到字段值
    47             String fieldName = field.getName();
    48             String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); // 利用字段名获取方法名
    49             Object fieldValue = null;
    50             try {
    51                 Method getMethod = c.getMethod(getMethodName); // 利用方法名得到方法
    52                 fieldValue = getMethod.invoke(f); // 反射调用方法
    53             } catch (Exception e) {
    54                 e.printStackTrace();
    55             }
    56             // 4.3 拼装sql
    57             if(fieldValue == null || (fieldValue instanceof Integer && (Integer)fieldValue == 0)) {
    58                 continue;
    59             }
    60             sb.append(" and ").append(fieldName);
    61             if(fieldValue instanceof String) {
    62                 if(((String) fieldValue).contains(",")) {
    63                     String[] values = ((String) fieldValue).split(",");
    64                     sb.append(" in (");
    65                     for (String string : values) {
    66                         sb.append("'").append(string).append("'").append(",");
    67                     }
    68                     sb.deleteCharAt(sb.length()-1);
    69                     sb.append(")");
    70                 } else {
    71                     sb.append("=").append("'").append(fieldValue).append("'");
    72                 }
    73             } else if(fieldValue instanceof Integer) {
    74                 sb.append("=").append(fieldValue);
    75             }
    76         }
    77         return sb.toString();
    78     }
    79 }

    输出结果:

    select * from user where 1=1 and id=10
    select * from user where 1=1 and userName='lucy' and age=18
    select * from user where 1=1 and email in ('liu@sina.com','zh@163.com','123@qq.com')

    Eclipse快捷键:

    shift + alt + s 调出有用命令,比如生成set get 方法。
    ctrl + d 删除整行
    ctrl + alt + down 复制整行

  • 相关阅读:
    MySQL数据库之索引
    python面向对象编程
    linux端口占用
    lintcode
    java基础
    lintcode
    linux 常用命令
    Docker & Kubernates
    angular preparation
    java 命令行
  • 原文地址:https://www.cnblogs.com/skyke/p/5023259.html
Copyright © 2011-2022 走看看