注解和反射 API 是 Java 提供的一种强大的工具,用于在运行时动态操作程序。它们使您能够在运行时获取类和方法的信息,以及动态创建和调用对象和方法
注解
介绍
注解(Annontation)是 Java5 引入的一种代码辅助工具, 它的核心作用是对类、方法、变量、参数和包进行标注,通过反射来访问这些标注信息,以此在运行时改变所注解对象的行为。Java 中的注解由内置注解和元注解组成
注释和注解
Java 注解又称 Java 标注,是 Java语音 5.0 版本开始支持加入源代码的特殊语法元数据
普通的注释在编译后的 class 文件中不存在的
而注解附加的信息则根据需要可以保存到 calss 文件中,甚至运行期加载的 Class 对象中
元注解介绍
- 创建注解: public @interface AnnotationName{}
- 元注解(描述注解的一种方式)
- @Retention 定义注解的生命周期:[source -> class -> runtime]
- @Documented 文档注解,会被 Javadoc 工具文档化
- @Inherited 是否让子类继承该注解
- @Target 描述了注解的应用范围
- TYPE:表示可以用来修饰类、接口、注解类型或枚举类型
- PACKAGE:可以用来修饰包
- PARAMETER:可以用来修饰参数
- ANNOTATION_TYPE:可以用来修饰注解类型
- METHOD:可以用来修饰方法
- FIELD:可以用来修饰属性(包括枚举常量)
- CONSTRUCTOR:可以用来修饰构造器
- LOCAL_VARIABLE:可用来修饰局部变量
创建一个注解
package com.dong.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD, ElementType.METHOD}) // 元注解,定义注解的修饰范围,可以设置多个
@Retention(RetentionPolicy.RUNTIME) // 元注解,定义注解的生命周期
public @interface Study { // 注解内部可以设置值,也可以不设置
String name();
int type();
String[] mores();
}
注解的创建方式:
- 配置元注解,由元注解来当前注解的作用范围和生命周期
- 注解中如果需要添加信息,可以用以上方式添加
- 注解信息支持java的基本数据结构
反射
反射(Reflection):在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射
反射的优缺点
- 通过反射可以使程序代码访问装载到 JVM 中的类的内部信息,获取已装载类的属性信息,获取已装载类的方法,获取已装载类的构造方法信息
- 反射提高了Java程序的灵活性和扩展性,降低耦合性,提高自适应能力
- 反射会对性能造成一定的影响,同时让代码的可读性变低
常用的反射 API
| 方法名 | 返回值 | 参数描述 |
|---|---|---|
| Class.ForName(String) | 获取类的元信息 | 当前类文件的具体位置 |
| 类.getClass() | 获取类的元信息 | 无 |
| clz.getDeclaredFields() | 获取当前类的所有属性 | 无 |
| setAccessibel(true) | 获取当前属性为可见 | true或者false |
| getMethods() | 获取类所有方法 | 无 |
| invoke(obj) | 获取反射执行方法 | 类的元信息 |
| getAnnotation(class) | 获取注解 | 需要获取的注解的Class |
代码示例
Study
package com.dong.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD, ElementType.TYPE, ElementType.METHOD}) // 元注解,定义注解的修饰范围,可以设置多个
@Retention(RetentionPolicy.RUNTIME) // 元注解,定义注解的生命周期
public @interface Study { // 注解内部可以设置值,也可以不设置
String name() default "jim"; // java基本类型
String[] mores();
}
Person
package com.dong.Entity;
import com.dong.annotation.Study;
@Study(mores = {"hard", "easy"})
public class Person {
private String name;
@Study(mores = {"hard", "easy"})
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Study(name = "tony", mores = {"hard", "easy"})
public String getStr() {
return "str";
}
}
ReflectionDemo
package com.dong.reflection;
import com.dong.Entity.Person;
import com.dong.annotation.Study;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectionDemo {
public static void main(String[] args) throws Exception {
// 实例1:通过反射获取到 Class 元信息
Person person = new Person();
Class<? extends Person> aClass = person.getClass(); // 类元信息
Class<?> aClass1 = Class.forName("com.dong.Entity.Person");
// 实例2:通过反射获取类名,包名
String name = aClass1.getName();
System.out.println(aClass1.getSimpleName());
System.out.println(name);
System.out.println(aClass);
System.out.println(aClass1);
// 实例3:类属性
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
// 实例4:获取到类属性具体的值
person.setName("jim");
person.setAge(18);
for (Field declaredField : declaredFields) {
declaredField.setAccessible(true); // 设置属性为可见
System.out.println(declaredField.get(person));
// System.out.println(declaredField);
}
// 实例4的另一种写法
Object p = aClass.newInstance();// 相当于反射中的实例化
for (Field declaredField : declaredFields) {
declaredField.setAccessible(true); // 设置属性为可见
if (declaredField.getName().equals("name")) {
declaredField.set(p, "tom");
} else {
declaredField.set(p, 18);
}
System.out.println(declaredField.get(p));
}
// 实例5:反射获取当前类的方法
Method[] methods = aClass.getMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
Method getStr = aClass.getMethod("getStr");
Object invoke = getStr.invoke(p); // 反射执行方法
System.out.println(invoke);
// 实例6:获得注解
Study study = aClass.getAnnotation(Study.class);
System.out.println(study);
String[] mores = study.mores();
for (String s : mores) {
System.out.println(s);
}
String name1 = study.name();
System.out.println("mores:" + mores + ";name:" + name1);
// 从方法上获取注解
for (Method method : methods) {
Study annotation = method.getAnnotation(Study.class);
if (annotation == null)
continue;
String annotationName = annotation.name();
String[] mores1 = annotation.mores();
for (String s : mores1) {
System.out.println(s);
}
System.out.println(annotationName);
// 从属性上获取注解
for (Field declaredField : declaredFields) {
Study annotation1 = declaredField.getAnnotation(Study.class);
if (annotation1 == null)
continue;
// 后续和上方相同
}
}
}
}
通过反射和注解实现简单的 Hibernate SQL 查询的功能
SqlState
package com.dong.annotation;
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 SqlState {
String value() default "";
}
Column
package com.dong.annotation;
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() default "";
}
Order
package com.dong.Entity;
import com.dong.annotation.Column;
import com.dong.annotation.SqlState;
@SqlState("t_order")
public class Order {
@Column
private Long id;// 和表字段一致
@Column("order_no")
private String orderNo; // 有值就作为条件
@Column("shop_id")
private int shopId;
@Column("user_name")
private String userName;
@Column("user_id")
private int userId;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getOrderNo() {
return orderNo;
}
public void setOrderNo(String orderNo) {
this.orderNo = orderNo;
}
public int getShopId() {
return shopId;
}
public void setShopId(int shopId) {
this.shopId = shopId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
}
GenerateSqlUtil
package com.dong.util;
import com.dong.annotation.Column;
import com.dong.annotation.SqlState;
import java.lang.reflect.Field;
public class GenerateSqlUtil {
public String query(Object tab) throws Exception {
StringBuffer sb = new StringBuffer();
// select 字段 from 表名 where 条件
sb.append("select");
StringBuffer sb2 = new StringBuffer();
// 反射机制
Class<?> aClass = tab.getClass();
SqlState annotation = aClass.getAnnotation(SqlState.class);// 表名
if (annotation == null)
throw new RuntimeException("注解缺失");
String tableName = annotation.value();
Field[] declaredFields = aClass.getDeclaredFields();
for (Field field : declaredFields) {
Column c = field.getAnnotation(Column.class);
if (c==null)
continue; // 没写就不生成查询字段
if (c.value().equals("")) { // 表字段名和属性名一致
String name = field.getName();
field.setAccessible(true);
Object o = field.get(tab);
if (o != null && Integer.parseInt(o.toString()) != 0) {
sb2.append(" and " + name + "=" + o);
sb.append(" " + name + ",");
}
}else {
sb.append(" " + c.value() + ",");
field.setAccessible(true);
Object o = field.get(tab);
if (o != null && Integer.parseInt(o.toString()) != 0) {
sb2.append(" and " + c.value() + "=" + o);
}
}
}
sb.deleteCharAt(sb.length()-1);
sb.append(" from " + tableName);
sb.append(sb2);
return sb.toString();
}
}
Test
package com.dong.util;
import com.dong.Entity.Order;
public class Test {
public static void main(String[] args) throws Exception {
GenerateSqlUtil generateSqlUtil = new GenerateSqlUtil();
Order order = new Order();
order.setOrderNo("123456");
String query = generateSqlUtil.query(order);
System.out.println(query);
}
}