概念:Java提供了一种原程序中的元素关联任何信息和任何元数据的途径和方法。
Java中常见注解
- JDK自带注解
- @Override:限定重写父类方法
- @Deprecated:标示某个程序元素(类、方法等)已过时
- @SuppressWarnings:被Annotation修饰的程序元素(以及该程序元素中的所有元素)取消显示指定的编译警告。
- @SafeVarargs:Java7中去除“堆污染”警告
- @FunctionalInterface:Java8中指定某个接口必须是函数式接口
注解分类
-
按照运行机制分
- 源码注解:注解只在源码中存在,编译成
.class
文件就不存在了 - 编译时注解:注解在源码和
.class
文件中都存在 - 运行时注解:在运行阶段起作用,甚至会影响运行逻辑的注解。
- 源码注解:注解只在源码中存在,编译成
-
按照来源分
- 来自JDK的注解
- 来自第三方的注解
- 我们自己定义的注解
自定义注解的语法要求
-
使用 @interface 关键字定义注解
-
成员以无参无异常方式申明
-
可以用 default 为成员指定一个默认值
-
成员类型是受限的,合法的类型包括原始类型及String,Class,Annotation,Enumeration
-
如果注解只有一个成员,则成员名必须取名为value(),在使用时可以忽略成员名和赋值号(=)
-
注解类可以没有成员,没有成员的注解称为标识注解
-
元注解
-
@Target():注解作用域
- ElementType
- CONSTRUCTOR:构造方法申明
- FIELD:字段申明
- LOCAL_VARIABLE:局部变量申明
- METHOD:方法申明
- PACKAGE:包申明
- PARAMETER:参数申明
- TYPE:类,接口
- ElementType
-
@Retention():生命周期
- RetentionPolicy
- SOURCE:只在源码显示,编译时会丢弃
- CLASS:编译时会记录到 class 中,运行时忽略
- RUNTIME:运行时存在,可以通过反射读取
- RetentionPolicy
-
@Inherited():允许子类继承
-
@Documented:生成 javadoc 时会包含注解
使用自定义注解的语法
- @<注解名>(<成员名1>=<成员值1>, <成员名2>=<成员值2>, ...)
@Description(desc="I am eyeColor", author="Mooc boy", age=18)
public String eyeColor(){
return "red" ;
}
解析注解
通过反射获取类、函数或成员上的运行时注解信息,从而实现动态控制程序运行的逻辑
// 1、使用类加载器加载类
Class clazz = Class.forName("com.anno.test.Child");
// 2、找到类上的注解
boolean isExist = clazz.isAnnotationPresent(Description.class);
// 3、拿到注解实例
if(isExist){
Description desc = (Description)clazz.getAnnotation(Description.class) ;
System.out.println(desc.value());
}
// 4、找到方法上的注解
Method[] methods = clazz.getMethods();
for(Method method : methods){
boolean isExist = method.isAnnotationPresent(Description.class);
if(isExist){
Description desc = (Description)method.getAnnotation(Description.class) ;
System.out.println(desc.value());
}
}
// 另外一种解析方法
for(Method method : methods){
Annotation[] annos = method.getAnnotations();
for(Annotation anno : annos){
if(anno instanceof Description){
Description desc = (Description)anno;
System.out.println(desc.value());
}
}
}
项目实战
需求
1、有一张用户表,字段包括用户ID,用户名,昵称,年龄,性别,所在城市,邮箱,手机号
2、方便的对每个字段或字段的组合条件进行检索,并打印出SQL
3、使用方式要足够简单,见代码示例。
@Table("user")
public class User {
@Column("id")
private int id;
@Column("user_name")
private String userName;
@Column("nick_name")
private String nickName;
@Column("age")
private int age;
@Column("city")
private String city;
@Column("email")
private String email;
@Column("mobile")
private String mobile;
.... // 省略getter和setter
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String value();
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String value();
}
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Filter {
public static String query(Object u) {
StringBuilder sqlStrBuilder = new StringBuilder();
// 1.获取到Class
Class c = u.getClass();
// 判断是否包含Table类型的注解
if (!c.isAnnotationPresent(Table.class)) {
return null;
}
// 2.获取到table的名字
Table t = (Table) c.getAnnotation(Table.class);
String tableName = t.value();
// 加入 where 1=1 ,是为了防止未来如果没有条件的情况下也不会报错
sqlStrBuilder.append("select * from ").append(tableName).append(" where 1=1");
Field[] fArray = c.getDeclaredFields(); // 获取类属性的所有字段,
// 3.遍历所有的字段
for (Field field : fArray) {
// 4.处理每个字段对应的sql
// 判断是否包含Column类型的注解
if (!field.isAnnotationPresent(Column.class)) {
continue;
}
// 4.1.拿到字段上面注解的值,即Column注解的值
Column column = field.getAnnotation(Column.class);
String columnName = column.value();
// 4.2.拿到字段的名
String filedName = field.getName();
// 获取相应字段的getXXX()方法
String getMethodName = "get" + filedName.substring(0, 1).toUpperCase() + filedName.substring(1);
// 字段的值
Object fieldValue = null;// 属性有int、String等,所以定义为Object类
try {
Method getMethod = c.getMethod(getMethodName);
fieldValue = getMethod.invoke(u);
} catch (Exception e) {
e.printStackTrace();
}
// 4.3.拼接sql
if (fieldValue == null || (fieldValue instanceof Integer && (Integer) fieldValue == 0)) {
continue;
}
sqlStrBuilder.append(" and ").append(filedName);
if (fieldValue instanceof String) {
if (((String) fieldValue).contains(",")) {
String[] values = ((String) fieldValue).split(",");
sqlStrBuilder.append(" in(");
for (String v : values) {
sqlStrBuilder.append("'").append(v).append("'").append(",");
}
sqlStrBuilder.deleteCharAt(sqlStrBuilder.length() - 1);
sqlStrBuilder.append(")");
} else {
sqlStrBuilder.append("=").append("'").append(fieldValue).append("'");
}
} else if (fieldValue instanceof Integer) {
sqlStrBuilder.append("=").append(fieldValue);
}
}
return sqlStrBuilder.toString();
}
}
public class Test {
public static void main(String[] args) {
User u1 = new User();
u1.setId(9); // 查询id为9的用户
User u2 = new User();
u2.setUserName("JTZeng"); // 模糊查询用户名为JTZeng的用户
u2.setAge(21);
User u3 = new User();
u3.setEmail("123@163.com,123@qq.com"); // 查询邮箱有任意一个的用户
String sql1 = Filter.query(u1); // 查询id为9的用户
String sql2 = Filter.query(u2); // 查询用户名为JTZeng和年龄为21的用户
String sql3 = Filter.query(u3); // 查询邮箱中有任意一个的用户
System.out.println(sql1);
System.out.println(sql2);
System.out.println(sql3);
}
}
网友评论