一、什么是反射?
在运行状态中,对于任意一个类,都能获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取信息以及动态调用对象的方法的功能就称为Java的反射机制。简单来讲,通过反射,该类对我们来说就是完全透明的,想要获取任何东西都可以。
想要使用反射机制,就必须要先获取到该类的字节码对象(.class),通过字节码对象文件,就能够通过该类中的方法获取到我们想要的信息(方法、属性、类名、父类名,实现的所有接口等等),每一个类对应着一个字节码文件也就对应着一个Class类型的对象,也就是字节码文件对象。
1、获取字节码对象的3种方式
(1) 通过Class类中的静态方法forName()直接获取到一个类的字节码文件对象
例: Class<?> aClass = Class.forName("com.example.homework.model.entity.Person");
即可获取到Person类的字节码对象
(2)通过类自身获取
Class<Person> p2Class = Person.class;
(3)通过类的实例获取
Person person = new Person();
Class<? extends Person> p3Class = person.getClass();
二、通过反射获取我们需要的信息
通过字节码对象创建实例
通过反射获取实例对象,直接使用字节码对象newInstance()即可创建一个实例
Class<Person> p2Class = Person.class;
Person p2 = p2Class.newInstance();
通过反射获取属性
获取所有属性,不包含私有属性: Field[] fields = p2Class.getFields();
获取特定属性,不包含私有属性: Field pai = p2Class.getField("pai");
如果需要获取私有属性需要使用下面的api
Field[] declaredFields = p2Class.getDeclaredFields();
Field idField = p2Class.getDeclaredField("id");
获取方法:
我们首先列出Person类中的两个测试方法:
// 公有方法
public String test1(){
System.out.println("test1,已经执行了!");
return "hello world!";
}
// 私有方法
private String privateTest2(String name,Double d){
System.out.println(name+"你好;工资:"+d);
return "hello"+name;
}
获取类的方法(不包含私有方法)并调用:
Method m1 = p2Class.getMethod("test1");
m1.invoke(p2);
这里为一个无参的方法,调用时无需指定参数类型,如果是有参方法,需要明确指定参数类型
获取类的私有方法,且该方法有参数:
Method privateTest2 = p2Class.getDeclaredMethod("privateTest2", String.class, Double.class);
privateTest2.setAccessible(true);
privateTest2.invoke(p2,"alice",900.85);
result:
alice你好;工资:900.85
三、应用
springdata jpa 中构造自定义转换类的时候,通过SQL查询出来的别名可以和我们的实体类对应,并在transformTuple中构建实体对象,该实体对象的属性即为SQL查询出来的字段,数值是统计出来的结果,
public class DefineResultTransformer<T> implements ResultTransformer, Serializable {
private static final long serialVersionUID = 775781526148172513L;
private Class<T> resultClazz;
private BeanUtilsBean beanUtilsBean;
public DefineResultTransformer(Class<T> resultClass){
this.resultClazz = resultClass;
this.beanUtilsBean = BeanUtilsBean.getInstance();
}
@Override
public Object transformTuple(Object[] tuple, String[] aliases) {
Object result = null;
Field[] fields = resultClazz.getDeclaredFields();
try {
result = resultClazz.newInstance();
for(int i =0 ;i<aliases.length; i++){
for(Field field : fields){
// 1、忽略大小写映射,如SQL查询字段为 "username" 可直接映射为Java实体类中userName属性
// 2、去掉"_" 映射,如SQL查询出的字段为 "user_name" 也可直接映射为Java实体类中userName属性
if(field.getName().equalsIgnoreCase(aliases[i].replaceAll("_",""))){
beanUtilsBean.setProperty(result,field.getName(),tuple[i]);
break;
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
@Override
public List transformList(List collection) {
return collection;
}
}
该工具转换类,接收一个结果类的字节码对象,然后通过反射获取该对象的属性,并和SQL查询查来的别名以及结果封装数据构造对象,最终完成SQL查询结果到Java对象的转换。