Java注解,来自小白的疑惑
平时开发我们会经常会引入第三方库,比如数据库框架ormlite,retrofit, 用的时候必须要用注解来映射到属性上去,这要是别人写的代码,自己有是小白,对这种写代码的人真是又羡慕有生气,羡慕他会用注解,感觉很酷的样子,生气就是不能写点人能看懂的代码吗,这是什么破玩意儿。下面贴出两个注解使用的截图
本人也有很大一段时间对注解保持着仇恨的态度,一看到注解就发怵。但是又不能放弃,毕竟这东西主流的一些框架都在用。于是今天从一个小例子来学习一下这个新手又爱又恨的东西
我们就拿一个通过注解来生成sql语句的例子来分析吧(这个例子是我在别人博客里面看到的 原出处请点击,我只是拿我的话再说一遍:
首先我们都知道,数据库是怎么回事,需要表名,和字段名,我们想定义这俩东西需要用到的注解:
/**
* 这个之前,我要先说这个注解的定义
* 写法是不是和类的定义很相似啊, @interface 就说明他是一个注解啦,
* 再看第一行,@Target 这个就是要告诉用注解的人,我这个的作用范围,
* 就这个例子来说 ElementType.TYPE 意思就是我这个注解你只能用来作用于类,而不能作用于变量或者方法上面
* 他的取值我们可以看一下 ElementType 这个enum里面的说明,一看就明白,不多说
*
* 第二行这个 @Retention 是用来规定这个注解要作用的时机,RetentionPolicy.RUNTIME 就是说我要在运行时
* 让他起作用,这也不难理解,你想啊,咱们的javabean 要存到数据库肯定是动态执行时的,所以选择 RetentionPolicy.RUNTIME
* 关于这个选项,可参见文档说明, 一看就明白,不多说了
* 不明白也不要紧,带着疑惑往下看
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
//关于这个value方法,如果你只有一个属性,当我们value()时,使用的时候就不需要指明是哪个属性了,直接填值就可以了
String value();
}
/**
* 这个是要用在字段上,所以我们选择 ElementType.FIELD
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String value();
}
一般的我们要往数据库里存东西,都会定义一个Java类来存储数据,里面的字段和数据库里的字段一一对应:
@Table("lijing") //上面我们提到了,@Table这个注解是给类用的(ElementType.TYPE),是不是解除了上面的疑惑 “lijing” 就是我们定义的表名
public class Person {
@Column("name") // 这里的name要和我们数据库的字段保持一致(这样说不太严谨,只要能和数据库的字段保持一一对应就行,不明白也不要纠结,那我们就写一致),在下面我们会明白
private String name;
@Column("address")
private String address;
@Column("email")
private String email;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
好了,我们现在测试一下
public class AnnotationTest {
public static void main(String[] args) {
Person person1 = new Person();
person1.setName("xiaoli");
Person person2 = new Person();
person2.setAddress("北京");
Person person3 = new Person();
person3.setEmail("5555@qq.com, 163333@163.com");
String query1 = query(person1);
String query2 = query(person2);
String query3 = query(person3);
System.out.println(query1);
System.out.println(query2);
System.out.println(query3);
}
/**
* 根据Person 这个对象里面的字段名和字段值来生成sql语句
* @param person
* @return
*/
public static String query(Person person) {
StringBuffer sql = new StringBuffer();
//这里用到了反射,不会反射的朋友也不要惊慌,在这我大致概括一下反射的使用过程
//1,先获取类对象 Class
//2,根据Class对象来获取Class里面的字段和方法
//3,在通过某个方法获取Object对象的字段的值或者调用Object的方法
//第一步,
Class clazz = person.getClass();
// 这里就是检测一下传进来的对象的类对象有没有用到我们定义的注解,没什么好说,记住这些方法
boolean exist = clazz.isAnnotationPresent(Table.class);
if (!exist) return null;
//获取注解的值,这里就是数据库表名
Table table = (Table) clazz.getAnnotation(Table.class);
String tableName = table.value();
//这里的" where 1 = 1"就是防止没有判断语句而导致sql语句不完整
sql.append("select * from ").append(tableName).append(" where 1 = 1");
//第二步 获取所有的字段,不管是public还是private,统统的拿到
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
//还是判断一下这个字段有没有用到注解
boolean existColunm = field.isAnnotationPresent(Column.class);
if (existColunm){
//数据库里面的字段
Column column = field.getAnnotation(Column.class);
String columnalue = column.value();
//到底看看这是javabean哪个属性
String fieldName = field.getName();
//根据属性名来找到get方法,下面的写法不是固定的,随机应变
String methodName = "get"+fieldName.substring(0,1).toUpperCase()+fieldName.substring(1);
try {
//看到了没,这就是第三步
Object invoke = clazz.getMethod(methodName).invoke(person);
if (invoke != null){
sql.append(" and ").append(columnalue).append("=").append(invoke);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
return sql.toString();
}
}
运行结果
select * from lijing where 1 = 1 and name=xiaoli
select * from lijing where 1 = 1 and address=北京
select * from lijing where 1 = 1 and email=5555@qq.com, 163333@163.com
其实呢些框架也是这么个用法,多用就熟练了