转载请注意:http://blog.csdn.net/wjzj000/article/details/53227352
个人GitHub上的俩个小小开源项目,希望各位大佬可以支持star一下。
https://github.com/zhiaixinyang/PersonalCollect (拆解GitHub上的优秀框架于一体,全部拆离不含任何额外的库导入)https://github.com/zhiaixinyang/MyFirstApp(Retrofit+RxJava+MVP)
开始之前咱们先简单梳理一下什么是注解:单纯说注解,注解本身没有任何的作用。简单说和注释没啥区别,而它有作用的原因是注解解释类。也就是相关对注解进行解释的特定类。一般这些类使用反射的方式完成注解的一些业务。
今天记录一下关于注解的一个demo,效果是通过注解的方式快速生成SQL语句。
首先是注解类:Table,Column(分别代表:表和列)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String value();
}
@Target:
用于描述注解的使用范围(即:被描述的注解可以用在什么地方)注解(annotation)可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在注解类型的声明中使用了target可更加明晰其修饰的目标。
取值(ElementType)有:
ElementType.ANNOTATION_TYPE
可以应用于注释类型。ElementType.CONSTRUCTOR
可以应用于构造函数。ElementType.FIELD
可以应用于字段或属性。ElementType.LOCAL_VARIABLE
可以应用于局部变量。ElementType.METHOD
可以应用于方法级注释。ElementType.PACKAGE
可以应用于包声明。ElementType.PARAMETER
可以应用于方法的参数。ElementType.TYPE
可以应用于类的任何元素。
@Retention:
@Retention定义了该注解被保留的时间长短:某些注解仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的注解可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为注解与class在使用上是被分离的)。使用这个meta-Annotation可以对注解的“生命周期”限制。
作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)
取值(RetentionPoicy)有:
1.SOURCE:在源文件中有效(即源文件保留)
2.CLASS:在class文件中有效(即class保留)
3.RUNTIME:在运行时有效(即运行时保留)
@Documented:
用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。
@Inherited:
是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。注意:当@Inherited的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现。
PS:
解只有一个成员时,按规范写成value(),当然不这么写不会报错。
如果不设置默认这,那么使用注解时必须要传值
- 只有类可以被注解,因为接口或者抽象类并不能用new操作实例化;
- 被注解的类,必须至少提供一个公开的默认构造器(即没有参数的构造函数)。否者我们没法实例化一个对象。
紧接着是注解的使用类:Person(就是一个使用注解的Java bean)
@Table("person")
public class Person {
@Column("name")
private String name;
@Column("user_name")
private String useName;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUseName() {
return useName;
}
public void setUseName(String useName) {
this.useName = useName;
}
}
PS:
如果有两个以上参数:必须这么使用@Column(value="user_name")
最终是main()里的使用:
public static void main(String[] args) {
// TODO Auto-generated method stub
Person p1=new Person();
//设置表名和列名。PS:这里的变量名取得有点奇葩,大家请无视。
p1.setName("ai");
p1.setUseName("aiai");
String str=query(p1);
System.out.println(str);
}
public static String query(Person person){
StringBuilder sb=new StringBuilder();
//通过反射,获取Class对象
Class p=person.getClass();
//判断此Class是不是注解类
boolean exist=p.isAnnotationPresent(Table.class);
if(!exist){
return null;
}
//如果是,强制类型转换成Table
Table table=(Table)p.getAnnotation(Table.class);
String tableName=table.value();
//接下来便是取值拼接sql语句,并且重复这个过程。
sb.append("select * from ").append(tableName).append(" where 1=1");
Field[] fArray=p.getDeclaredFields();
for(Field field :fArray){
boolean fExist=field.isAnnotationPresent(Column.class);
if(!fExist){
return null;
}
Column column=field.getAnnotation(Column.class);
String cloumnName=column.value();
String fieldName=field.getName();
Object fieldValue=null;
//此处将生成getXX方法,用于下边通过反射执行对应的get方法拿到里边的返回值。
String getMethodName="get"+fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1);
try {
Method method=p.getMethod(getMethodName);
fieldValue=method.invoke(person);
} catch (Exception e) {
e.printStackTrace();
}
sb.append(" and ").append(cloumnName).append("=").append(fieldValue);
}
return sb.toString();
}
PS:
可能这样写感觉没什么用。但是如果这是一个库。那么我们只需要按照库的要求。使用相应的注解,按照特定的格式完成注解。就可以直接获取到最终结果了。比如可以在库中写一个工具类,直接调用这个工具类,传递所需的参数,返回最终拼接的sql语句而且,不会出现任何的语句错误。
想想是不是还有点小激动呢?
最近有结合安卓中的ButterKnife写了一遍关于注解的梳理,有兴趣的看官可以去捧个场:
http://blog.csdn.net/wjzj000/article/details/54177914
运行效果:
最后希望各位看官可以star我的GitHub,三叩九拜,满地打滚求star:
https://github.com/zhiaixinyang/PersonalCollect
https://github.com/zhiaixinyang/MyFirstApp