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 复制整行

原文地址:https://www.cnblogs.com/skyke/p/5023259.html