摘要:
java注解:java 提供了一种源程序中的 元素 关联任何信息和任何元数据的途径和方法。
学习注解的目的:
- 能够读懂别人写的代码,特别是框架相关的代码
- 让编程更加简洁,代码更加清晰
- 让别人高看一眼,特别是会使用自定义注解
目录:
-
java中的常见注解
-
注解分类
-
自定义注解
-
注解应用实践
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 复制整行