注解篇
注解与注释
所谓注释,就是在Java代码编写中插入一段说明文字,记录参数、方法的定义与使用等等,在编译后的class文件中是不存在的。
所谓注解,又称作Java标注,是Java语言5.0版本开始引入的一种特殊语法元数据。可根据需要保存到class文件中,甚至运行期加载的Class对象中。它是交由Java编译器阅读的,其本身对程序的执行是没有影响的,其本质就是一个继承了 Annotation 接口的接口。了解注解,以下几点必不可少:
元注解
- @Retention:定义注解的生命周期
- @Documented:文档注释,会被Javadoc工具文档化
- @Inherited:是否让子类继承该注解
- @Target:描述了注解的应用范围
元注解的作用就是负责注解其他注解,以上四个,最常用的是@Retention和@Target,分别来说明以下:
@Retention:它描述了注解保留的时间范围,定义在RetentionPolicy枚举中,有以下三种,一般来说,我们自定义的注解希望在运行期间依旧生效,所以一般定义RUNTIME
- SOURCE:源文件保留
- CLASS: 编译期间保留(默认)
- RUNTIME :运行期间保留
@Target:它描述了注解的应用范围,包括以下几种备选项:
- TYPE, // 修饰类、接口(包括注释类型)、枚举类
- FIELD, // 修饰成员变量(包括枚举常量)
- METHOD, // 修饰方法
- PARAMETER, // 修饰形式参数
- CONSTRUCTOR, // 修饰构造器
- LOCAL_VARIABLE, // 修饰局部变量
- ANNOTATION_TYPE, // 修饰注解类型
- PACKAGE, // 修饰包
- TYPE_PARAMETER, // 泛型使用,JDK 1.8 新增
- TYPE_USE // 在任何用到类型的地方使用,JDK 1.8 新增
创建注解
掌握了元注解之后,就可以创建自定义注解了,使用**public @interface AnnotationName{}**方式创建。注解支持添加信息,Java的基本数据结构都支持,可如下添加:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.TYPE})
public @interface User{
String name() default "Eric";
String[] mores();
int type();
}
Java常用注解
除了我们自定义注解,Java中也提供了一些常用的Annotation,比如
- @Deprecated:所标注内容,不再被建议使用。
- @Override:表示该方法覆盖父类中的方法,只能标注方法。
- @SuppressWarnings:所标注内容产生的警告,编译器会对这些警告保持静默。
了解了注解的基本内容之后,就会开始考虑怎么让注解生效,开始让代码真正利用注解实现一些功能,这就需要应用反射的知识了。
反射篇
反射:在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任何一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能成为Java语言的反射。
掌握常见的反射api就可以使用反射来实现一些功能:
反射的优缺点
- 通过反射可以使程序访问到装载在JVM中的类的内部信息,获取已装载类的属性信息、类的方法、构造方法信息等。
- 反射的应用降低了耦合性,提高自适应能力。
- 反射会对性能造成一定的影响(这个影响要看具体业务,可能会有几百毫秒的延迟,对于要求不高的程序其实影响不大),同时让代码的可读性变低(主要缺点)
这里给出一个反射应用的例子:
反射的应用
功能描述:获取一条查询sql语句,包括实体类使用注解的所有字段
具体实现:
定义两个注解:@SqlState (获取表名),@Column (获取字段)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface SqlState {
String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String value() default "";
String name() default "";
}
创建实体类Student
@SqlState("student_t")
public class Student {
@Column
private Long id;
@Column("order_no")
private String name;
@Column("student_id")
private int studentId;
@Column
private String grade;
@Column("school_name")
private String schoolName;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getStudentId() {
return studentId;
}
public void setStudentId(int studentId) {
this.studentId = studentId;
}
public String getGrade() {
return grade;
}
public void setGrade(String grade) {
this.grade = grade;
}
public String getSchoolName() {
return schoolName;
}
public void setSchoolName(String schoolName) {
this.schoolName = schoolName;
}
}
创建组装sql类
public class GenerateSqlUtil {
public String query(Object obj) throws Exception{
StringBuffer sb = new StringBuffer();// 拼接sql主体
sb.append("select");
StringBuffer sb2 = new StringBuffer();// 拼接查询字段
Class<?> clz = obj.getClass();
SqlState annotation = clz.getAnnotation(SqlState.class);// 获取SqlState注解
if (annotation==null)
throw new RuntimeException("注解缺失!");// 必须有表名
String tableName = annotation.value();// 拿到表名
Field[] declaredFields = clz.getDeclaredFields();// 获取类中的属性
for (Field field : declaredFields) {
Column column = field.getAnnotation(Column.class);// Column
if (column==null)
continue; //没写不生成查询字段
if (column.value().equals("")){ // 拿到默认值的column说明实体类字段名与数据库定义的名字一致
String name = field.getName();
field.setAccessible(true);// 设置属性可见
Object o = field.get(obj);
if (o != null){// 下面只粗浅的判断参数值的类型
if(o instanceof String){
sb2.append(" and " + name + "='" + o + "'");
} else if(o instanceof Integer && Integer.parseInt(o.toString()) != 0){
sb2.append(" and " + name + "=" + o);
}
}
sb.append(" " + name + ",");
}else{ // 为column注解value赋值的获取对应的值
sb.append(" " + column.value() + ",");
field.setAccessible(true);
Object o = field.get(obj);
if (o !=null && Integer.parseInt(o.toString()) != 0){
sb2.append(" and " + column.value() + "=" + o);
}
}
}
sb.deleteCharAt(sb.length()-1);// 去掉多余的“,”
sb.append(" from " + tableName + " where 1=1");
sb.append(sb2);
return sb.toString();
}
}
编写测试类
public class Test {
public static void main(String[] args) throws Exception {
GenerateSqlUtil generateSqlUtil = new GenerateSqlUtil();
Student student = new Student();
student.setStudentId(1);
student.setName("张三");
String s1 = generateSqlUtil.query(student);
System.out.println(s1);// 输出结果:select id, name, student_id, grade, school_name from student_t where 1=1 and name='张三' and student_id=1
}
}
虽然使用这种暴力拆解的方式实现功能不便于阅读,但对于反射的学习还是很有帮助的。