常用反射方法

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 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_42798851/article/details/115335475