1、常用反射方法
- 判断是否包含某个方法
/**
* 判断本class内是否包含某个属性
* getDeclaredField 包括类内部public protect defalut 以及private属性
* getFiled 只包含public 属性 但是也包含继承的属性
* 同样 getDeclaredMethod 可以判断是否包含某个方法 类似于 getDeclaredField
* getMethod 类比于getFiled 只包含public 方法,但是也包含继承的方法
*/
public static boolean containFiled(Class<?> clazz, String method) {
try {
clazz.getDeclaredField(method);
} catch (NoSuchFieldException e) {
return false;
}
return true;
}
public static boolean containFileds(Class<?> clazz, List<String> methods) {
for (String method : methods) {
try {
clazz.getDeclaredField(method);
} catch (NoSuchFieldException e) {
return false;
}
}
return true;
}
- 反射复制Java Bean
/**
* 思路
* 1. 传参包括2个对象,分别为源对象和目标对象,可以属于不同的类
* 2. 获取两对象的Class
* 3. 获取两对象的属性
* 4. 遍历目标对象的属性,拼接set方法
* 5. 遍历源对象的属性,拼接get方法
* 6. 判断属于相同方法执行 target.setXxx(souce.getXxx)
* set的时候判断参数的类型
* @Param source 源对象
* @Param target 目标对象
*/
public static void copy(Object source, Object target) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<?> sourceClass = source.getClass();
Class<?> targetClass = target.getClass();
Field[] sourceClassFields = sourceClass.getDeclaredFields();
Field[] targetClassFields = targetClass.getDeclaredFields();
for (Field targetClassField : targetClassFields) {
String targetFirstUpper = targetClassField.getName().substring(0, 1).toUpperCase();
String setName = "set" + targetFirstUpper + targetClassField.getName().substring(1);
for (Field sourceClassField : sourceClassFields) {
if (targetClassField.getName().equals(sourceClassField.getName())) {
String sourceFirstUpper = sourceClassField.getName().substring(0, 1).toUpperCase();
String getName = "get" + sourceFirstUpper + sourceClassField.getName().substring(1);
Method getMethod = sourceClass.getMethod(getName);
Method setMethod = targetClass.getMethod(setName, sourceClassField.getType());
Object invoke = getMethod.invoke(source);
if(invoke != null) {
setMethod.invoke(target, new Object[]{invoke});
}
break;
}
}
}
}
- 反射执行某个方法(只适用于参数个数不同的场景)
/**
* 反射执行方法
* @param object 执行的对象
* @param methodName 方法名
* @param params 参数数组
* @return
*/
public static Object invoke(Object object ,String methodName,Object[] params){
Class<?> objectClass = object.getClass();
// public 方法 包括父类集成的方法
Object result = null;
Method[] methods = objectClass.getMethods();
for (Method method : methods) {
if(method.getName().equals(methodName)){
try {
result = method.invoke(object, params);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
return result;
}
————- 2022-08-09 补充 ——————-
- 反射执行方法修改
以上在执行反射方法的时候可能照成异常的情况。
异常的原因是由于 参数个数相同但是类型不同的情况,例如评论中的示例 getAge(String str) 和 getAge(Integer age)。
以下为新增反射执行方法,对参数进行了判断。
/**
* 反射执行方法
*
* @param object 执行的对象
* @param methodName 方法名
* @param paramsMaps 参数数组
* @return
*/
public static Object invoke(Object object, String methodName, LinkedHashMap<Class<?>, Object> paramsMaps) {
Class<?> objectClass = object.getClass();
// public 方法 包括父类集成的方法
Object result = null;
Method[] methods = objectClass.getMethods();
for (Method method : methods) {
// 检验方法名是否相同
if (method.getName().equals(methodName)) {
Class<?>[] parameterTypes = method.getParameterTypes();
// 判断是否无参数
if (parameterTypes.length != 0) {
if (parameterTypes.length == paramsMaps.size()) {
LinkedList<Class<?>> classes = new LinkedList<>(paramsMaps.keySet());
boolean flag = true;
// 检验参数类型是否相同
for (int i = 0; i < parameterTypes.length; i++) {
Class<?> parameterType = parameterTypes[i];
Class<?> aClass = classes.get(i);
System.out.println("parameterType " + parameterType.getName());
System.out.println("aClass " + aClass.getName());
if (parameterType != aClass) {
flag = false;
break;
}
}
if (flag) {
try {
Object[] objs = new Object[parameterTypes.length];
int i = 0;
for (Object obj : paramsMaps.values()) {
objs[i] = obj;
i++;
}
result = method.invoke(object, objs);
break;
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
} else {
try {
result = method.invoke(object);
// 跳出遍历
break;
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
return result;
}
代码说明:
1、无参的情况进行判断,Class<?>[] parameterTypes = method.getParameterTypes(); 如果参数的类型数量为0 ,直接按照之前的方式进行执行。
2、存在参数的情况,为了保证多个参数的顺序性,这里使用了LinkedHashMap<Class<?>,Object> ,key代表参数的类型,value代表传入的值。
3、多个参数的情况,先比较参数的个数,如果个数相同那么按照顺序比较参数的类型。
注意: 这里没有对类加载器进行比较,有感兴趣的朋友可以尝试下。
2、测试
- 测试Java Bean
public class User {
private String id;
private String userName;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@Override
public String toString() {
return "User{" +
"id='" + id + '\'' +
", userName='" + userName + '\'' +
'}';
}
}
- 测试执行
其中User 和 User4对象参数相同,方法相同。
//判断对象
User user = new User();
boolean getId = containField(user.getClass(), "getId");
boolean getId2 = containFields(user.getClass(), Arrays.asList("getId","getUserName"));
System.out.println(getId +"========="+getId2);
User user2 = new User();
// 执行方法
Object setId = invoke(user2, "setId", new Object[]{"1234"});
System.out.println(user2);
// java copy
User user3 = new User();
user3.setId("123");
User4 user4 = new User4();
copy(user3,user4);
System.out.println(user4);
测试结果
User{id='1234', userName='null'}
User{id='1234', userName='null'}
User{id='123', userName='null'}
- 2022-08-09 补充测试
User user2 = new User();
LinkedHashMap<Class<?>, Object> paramMaps = new LinkedHashMap<>();
paramMaps.put(String.class, "1234");
Object setId = invoke(user2, "setId", paramMaps);
System.out.println(user2);
Object getId = invoke(user2, "getId", null);
System.out.println(getId);
测试结果:
parameterType java.lang.Integer
aClass java.lang.String
parameterType java.lang.String
aClass java.lang.String
User{id='1234', username='null', password='null', salt='null', address='null', phone='null', createTime='null', updateTime='null', spare1='null', spare2='null', spare3='null'}
1234
至此,反射方法执行完成。
版权声明:本文为weixin_42798851原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。