- 注解是一种在原程序中的元素关联任何信息和任何元数据的途径和方法。
- 在 Java 项目中、尤其是框架相关的代码,注解往往很常用,可以让代码更简洁清晰。
常见注解
JDK 自带注解
- @Override:覆盖父类、实现接口的方法;
- @Deprecated:方法已过时(可以被覆盖,但调用时会提示过时);
- @SuppressWarnings("deprecation"):忽略方法过时的提示;
- ...
第三方注解
Spring 注解:
- @Autowired
- @Service
- @Repository
- @Controller
- ...
Mybatis 注解:
- @InsertProvider
- @UpdateProvider
- @Options
- ...
注解分类
按运行机制分类:
- 源码注解:注解只在源码中存在,编译成 .class 文件就不存在了;
- 编译时注解:在源码和 .class 文件中都存在;
- 运行时注解:在运行阶段还起作用,甚至会影响运行逻辑。
按来源分类:
- JDK 自带注解
- 第三方(框架)注解
- 自定义注解
- 元注解
自定义注解
定义注解
// 元注解
@Target({ElementType.METHOD, ElementType.TYPE}) // 声明注解适用范围:构造方法、字段、局部变量、方法、包、参数、类和接口等
@Retention(RetentionPolicy.RUNTIME) // 生命周期:SOURCE(只存在于源码)、CLASS(记录到 class 中,运行时忽略)、RUNTIME(运行时存在,可反射)
@Inherited // 在父类上添加了注解允许子类继承
@Documented // 生成 JavaDoc 时包含注解信息
public @interface Description { // @interface 关键字定义注解
// 注解可以没有成员,没有成员的注解称为标识注解
// 如注解只有一个成员则必须取名为 value(),使用时可以忽略成员名和复制号(=)
String desc(); // 注解成员以无参无异常方式声明
String author(); // 成员类型受限:String、Class、Annotation、Enumeration
int age() default 18; // 可以为成员指定默认值
}
使用自定义注解
@Desciption(desc="my test", author="YWH", age=17)
public String test() {
return "OK";
}
解析注解
通过反射获取类、方法或成员上的运行时注解信息(只能是运行时注解),从而实现动态控制程序运行的逻辑。
try {
Class c = Class.forName("com.ywh.test.Test");
boolean isExist = c.isAnnotationPresent(Description.class); // 判断类上是否添加了注解
if (isExist) {
Description d = c.getAnnotation(Description.class);
System.out.println(d.value());
}
Method[] ms = c.getMethods();
for (Method m: ms) {
boolean isMExist = m.isAnnotationPresent(Description.class);
if (isMExist) {
Description d = (Description) m.getAnnotation(Description.class);
System.out.println(d.value());
}
}
// 另一种解析方法
for(Method m: ms) {
Annotation[] as = m.getAnnotations();
for (Annotation a: as) {
if (a instanceof Description) {
Description d = (Description) a;
System.out.println(d.value());
}
}
}
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
实例:使用注解实现 ORM 框架(生成查询语句)
Table.java
// 表注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String value();
}
Column.java
// 字段注解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String value();
}
定义测试数据模型
@Table("user")
public class Filter {
@Column("id")
private int id;
@Column("user_name")
private String name;
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
其对应的 MySQL 表:
CREATE TABLE user (
id INT,
user_name VARCHAR(32)
);
Test.java
public class Test {
public static void main(String[] args) {
Filter f1 = new Filter();
f1.setId(1);
Filter f2 = new Filter();
f2.setName("ywh");
String sql1 = query(f1);
String sql2 = query(f2);
System.out.println(sql1);
System.out.println(sql2);
}
private static String query(Filter f) {
// 待拼装的 SQL 语句
StringBuilder sb = new StringBuilder();
// 1. 获取类类型,通过反射获取类注解、从 Table 注解中提取出对应的数据库表名
Class c = f.getClass();
boolean exists = c.isAnnotationPresent(Table.class);
if (!exists) {
return null;
}
Table t = (Table) c.getAnnotation(Table.class);
String tableName = t.value();
sb.append("SELECT * FROM ").append(tableName).append(" WHERE 1 = 1");
// 2. 遍历数据模型类的所有字段(对应数据库表的字段),拼装查询条件
Field[] fArray = c.getDeclaredFields();
for (Field field : fArray) {
// 2.1 获取字段名:从 Column 注解中获取字段对应的数据库字段名
boolean fExists = field.isAnnotationPresent(Column.class);
if (!fExists) {
continue;
}
Column column = field.getAnnotation(Column.class);
String columnName = column.value();
// 2.2 获取字段值:根据类的字段名拼装出 getter 方法,求得字段值
String fieldName = field.getName();
String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
Object fieldValue = null;
try {
Method getMethod = c.getMethod(getMethodName);
fieldValue = getMethod.invoke(f);
} catch (Exception e) {
e.printStackTrace();
}
// 2.3 拼装 SQL(暂不处理空值和等于 0 的值)
if (fieldValue == null ||
(fieldValue instanceof Integer && (Integer) fieldValue == 0)
) {
continue;
}
sb.append(" AND ").append(columnName);
// 字段值为 String 类型
if (fieldValue instanceof String) {
// 以逗号割开,则解析为 IN 条件
if (((String) fieldValue).contains(",")) {
String[] values = ((String) fieldValue).split(",");
sb.append(" IN (");
for (String v : values) {
sb.append("'").append(v).append("'").append(",");
}
sb.deleteCharAt(sb.length() - 1); // 删除最后一个逗号
sb.append(")");
} else
sb.append(" = ").append("'").append(fieldValue).append("'");
}
// 字段值为其他类型
else if (fieldValue instanceof Integer) {
sb.append(" = ").append(fieldValue);
}
}
return sb.toString();
}
}