java反射保存表_Java反射妙用

本文详细介绍了Java反射的应用,包括如何通过反射创建和调用对象、获取类属性和方法。通过实例展示了如何在项目中使用反射处理不同业务数据,避免大量重复代码。此外,还探讨了反射的常见API、注意事项以及可能遇到的问题,如递归处理DTO、排除静态变量、注解判断等。
摘要由CSDN通过智能技术生成

一、背景

开发过程中难免遇到处理不同Java类的数据,由于数据结构不一样,很难统一去处理,如果是分开处理会划分很多精力和写大量重复代码。

项目上真实案例:

不同类型的业务要做一个统一的方法处理,需要拿到这些数据,但这些业务数据结构不同,一种业务是多个类型的表组成,很难统一去处理,如果用上反射就可以用统一的方法拿到不同业务类型的代码!

二、代码演示

基础代码示例

正面代码

Apple apple = new Apple(); //直接初始化,「正射」

apple.setPrice(4);

而反射则是一开始并不知道我要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了。

这时候,我们使用 JDK 提供的反射 API 进行反射调用:

Class clz = Class.forName("com.reflect.Apple");

Method method = clz.getMethod("setPrice", int.class);

Constructor constructor = clz.getConstructor();

Object object = constructor.newInstance();

method.invoke(object, 4);

上面两段代码的执行结果,其实是完全一样的。但是其思路完全不一样,第一段代码在未运行时就已经确定了要运行的类(Apple),而第二段代码则是在运行时通过字符串值才得知要运行的类(com.chenshuyi.reflect.Apple)。

简单例子

public class Apple {

private int price;

public int getPrice() {

return price;

}

public void setPrice(int price) {

this.price = price;

}

public static void main(String[] args) throws Exception{

//正常的调用

Apple apple = new Apple();

apple.setPrice(5);

System.out.println("Apple Price:" + apple.getPrice());

//使用反射调用

Class clz = Class.forName("com.api.Apple");

Method setPriceMethod = clz.getMethod("setPrice", int.class);

Constructor appleConstructor = clz.getConstructor();

Object appleObj = appleConstructor.newInstance();

setPriceMethod.invoke(appleObj, 14);

Method getPriceMethod = clz.getMethod("getPrice");

System.out.println("Apple Price:" + getPriceMethod.invoke(appleObj));

}

}

从这个简单的例子可以看出,一般情况下我们使用反射获取一个对象的步骤:

获取类的 Class 对象实例

Class clz = Class.forName("com.zhenai.api.Apple");

根据 Class 对象实例获取 Constructor 对象

Constructor appleConstructor = clz.getConstructor();

使用 Constructor 对象的 newInstance 方法获取反射类对象

Object appleObj = appleConstructor.newInstance();

而如果要调用某一个方法,则需要经过下面的步骤:

获取方法的 Method 对象

Method setPriceMethod = clz.getMethod("setPrice", int.class);

利用 invoke 方法调用方法

setPriceMethod.invoke(appleObj, 14);

到这里,我们已经能够掌握反射的基本使用。但如果要进一步掌握反射,还需要对反射的常用 API 有更深入的理解。

在 JDK 中,反射相关的 API 可以分为下面几个方面:获取反射的 Class 对象、通过反射创建类对象、通过反射获取类属性方法及构造器。

项目上实战

项目上也相同的需求,以下代码便是用简单的衍生过来的

传入一个dto(包含不同的类型数据集合),每一个DTO都会继承BaseDTO,可以通过instanceof判断是否有子数据或者集合,在做递归操作获取数据

每一个DTO都会继承BaseDTO,可以通过instanceof判断是否有子数据或者集合,在做递归操作获取数据

也可以查询字段注释或者是常量来根据业务逻辑做进一步操作

public static void getClaimFiled(T object) {

Field[] fields = object.getClass().getDeclaredFields();

for (Field field : fields) {

String type = field.getType().toString();

// 常量数据不存

if((type.endsWith("java.lang.String") || type.endsWith("java.lang.Long"))

&& Modifier.isStatic(field.getModifiers())){

// 常量

continue;

}

// 获取注释

Transient annotation = field.getAnnotation(Transient.class);

if (annotation != null) {

continue;

}

// 判断类型 是 集合还是什么 继承了 baseDto?

Object value = getFieldValue(field, object);

// 集合 继续遍历

if (value instanceof Collection) {

getClaimFiledList((List)value, list, ss);

} else if (value instanceof BaseDTO) {

getClaimFiled(value, list, ss);

} else {

// 获取到数据变可以存值 此处省。

// field.getName()

// value

}

}

}

如果是集合类型则需要循环

/**

* 数据集合的获取方式

*/

public static void getClaimFiledList(List object) {

for (Object o : object) {

getClaimFiled(o, list, ss);

}

}

获取数据的值

/**

* 通过列获取值

*

* @param field 列

* @param bean 类型

* @return 值

*/

public static Object getFieldValue(Field field, Object bean) {

String fieldGetName = this.parGetName(field.getName());

Class> cls = bean.getClass();

Method[] methods = cls.getDeclaredMethods();

try {

// 没有get方法

if (!checkGetMet(methods, fieldGetName)) {

return null;

}

Method fieldGetMet = cls.getMethod(fieldGetName, new Class[]{});

Object value = fieldGetMet.invoke(bean, new Object[]{});

String fieldType = field.getType().getSimpleName();

if ("Date".equals(fieldType)) {

value = fmtDate((Date) value);

}

// 金额字段

if ("BigDecimal".equals(fieldType)) {

if (value != null) {

BigDecimal bd = (BigDecimal) value;

value = bd.setScale(NumberConstants.NUM_INT_6, BigDecimal.ROUND_HALF_UP);

}

}

return value;

} catch (NoSuchMethodException | InvocationTargetException e) {

logger.error("获取不对值{}", e);

} catch (IllegalAccessException e) {

logger.error("IllegalAccessException{}", e);

}

return null;

}

拼接某属性的 get方法,拿到方法名

/**

* 拼接某属性的 get方法

*

* @param fieldName 字段名

* @return String 结果

*/

public static String parGetName(String fieldName) {

if (null == fieldName || "".equals(fieldName)) {

return null;

}

int startIndex = 0;

if (fieldName.charAt(0) == UNDERLINE) {

startIndex = 1;

}

return "get"

+ fieldName.substring(startIndex, startIndex + 1).toUpperCase()

+ fieldName.substring(startIndex + 1);

}

三、拓展

获取反射中的Class对象

在反射中,要获取一个类或调用一个类的方法,我们首先需要获取到该类的 Class 对象。

在 Java API 中,获取 Class 类对象有三种方法:

第一种,使用 Class.forName 静态方法。当你知道该类的全路径名时,你可以使用该方法获取 Class 类对象。

Class clz = Class.forName("java.lang.String");

第二种,使用 .class 方法。

这种方法只适合在编译前就知道操作的 Class。

Class clz = String.class;

第三种,使用类对象的 getClass() 方法。

String str = new String("Hello");

Class clz = str.getClass();

通过反射创建类对象

通过反射创建类对象主要有两种方式:通过 Class 对象的 newInstance() 方法、通过 Constructor 对象的 newInstance() 方法。

第一种:通过 Class 对象的 newInstance() 方法。

Class clz = Apple.class;

Apple apple = (Apple)clz.newInstance();

第二种:通过 Constructor 对象的 newInstance() 方法

Class clz = Apple.class;

Constructor constructor = clz.getConstructor();

Apple apple = (Apple)constructor.newInstance();

通过 Constructor 对象创建类对象可以选择特定构造方法,而通过 Class 对象则只能使用默认的无参数构造方法。下面的代码就调用了一个有参数的构造方法进行了类对象的初始化。

Class clz = Apple.class;

Constructor constructor = clz.getConstructor(String.class, int.class);

Apple apple = (Apple)constructor.newInstance("红富士", 15);

四、可实现内容。

1、通过模版T来获取T传入的所有数据

2、通过空的模版T来反射得到表面来使用动态sql来查询所以相关的数据

说明:表上都会有@Table注解

/**

* 查询DTO内包含的表名

*

* @param object 模版DTO

* @return key为表名 value为DTO

*/

public static Map getDTOFiled(Object object) {

Map map = new HashMap<>();

Field[] fields = object.getClass().getDeclaredFields();

for (Field field : fields) {

// 常量数据不存

if(Modifier.isStatic(field.getModifiers())){

continue;

}

// 判断类型 是 集合还是什么 继承了 baseDto?

getDTOFieldValue(field, map);

}

return map;

}

// 获取值

public static void getDTOFieldValue(Field field, Map map) {

// 通过字段属性再次反射

Type type = field.getGenericType();

try {

Class cl = Class.forName(type.getTypeName());

Table table = (Table)cl.getAnnotation(Table.class);

if (table != null) {

// 获取数据表名

map.put(table.name(), cl);

return;

}

} catch (ClassNotFoundException e) {

logger.error("找不到类名 {}", e);

}

if ("java.util.List".equals(field.getType().getTypeName())) {

Class clazz = (Class)((ParameterizedType) type).getActualTypeArguments()[0];

//获取类型的类型参数类型。 你可以去查看jdk帮助文档对ParameterizedType的解释。

// getFiledName(cl1z);

Table table = (Table)clazz.getAnnotation(Table.class);

if (table != null) {

// 获取数据表名

map.put(table.name(), clazz);

return;

}

}

}

获取DTO中的某区域的数据, 方法开始回住的根据某个参数来获取存值,之后在便利满足条件的数据

由于次对象会基础父类BaseDto.class,一些数据存在父类中,不能直接获取到。

public static void getHeaderFiled(Object object) {

Class> tmpCl = object.getClass();

// 获取继承的父类

Class> baCl = tmpCl.getSuperclass();

if (baCl == BaseDto.class) {

// 找到 获取头的属性

Field field = baCl.getDeclaredField("ceader");

String fieldGetName = DtoRefUtil.parGetName(field.getName());

try {

Method fieldGetMet = baCl.getMethod(fieldGetName, new Class[]{});

Object value = fieldGetMet.invoke(object, new Object[]{});

Class> cls = value.getClass();

Table table = cls.getAnnotation(Table.class);

getHeaderFiled(value);

} catch (NoSuchMethodException | InvocationTargetException e) {

logger.error("获取不对值{}", e);

} catch (IllegalAccessException e) {

logger.error("IllegalAccessException{}", e);

}

}

Field[] fields = object.getClass().getDeclaredFields();

for (Field field : fields) {

// 常量数据不存

if(Modifier.isStatic(field.getModifiers())){

continue;

}

// 不存头数据 前已经存过了

if ("ceader".equals(field.getName())) {

continue;

}

// 判断类型 是 集合还是什么 继承了 baseDto?

Object value = getFieldValue(field, object);

// 集合 继续遍历

if (value instanceof BaseDTO) {

Class> cls = value.getClass();

getClaimHeaderFiled(value, list);

} else {

// 获取注释

Transient annotation = field.getAnnotation(Transient.class);

if (annotation != null) {

continue;

}

for (Field field : fields) {

// 常量数据不存

if(Modifier.isStatic(field.getModifiers())){

continue;

}

// 判断类型 是 集合还是什么 继承了 baseDto?

Object value = getFieldValue(field, object);

// 集合 继续遍历

if (value instanceof BaseDTO) {

Class> cls = value.getClass();

// 获取数据表名

Table table = cls.getAnnotation(Table.class);

getHeaderFiled(value);

} else {

// 获取注释

Transient annotation = field.getAnnotation(Transient.class);

if (annotation != null) {

continue;

}

// field.getName()

// String.valueOf(value)

}

});

}

}

}

五、零碎知识和可能遇到的问题

1、递归调用DTO和继承的父DTO的值

Class> tmpCl = object.getClass();

// 获取继承的父类

Class> baCl = tmpCl.getSuperclass();

2、递归调用DTO和成员变量的值(可能是list)

value instanceof BaseDTO // value instanceof Collection

3、通过注释判断是否要获取

Transient annotation = field.getAnnotation(Transient.class);

4、静态变量排除

Modifier.isStatic(field.getModifiers())

5、获取成员变量对应的属性(非基本属性类型)

Class clazz = (Class)((ParameterizedType) type).getActualTypeArguments()[0];

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值