Java 反射调用

Java 反射调用

1. 反射定义

是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

2.反射用途

在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应用开放,这时候就可以利用Java的反射机制通过反射来获取所需的私有成员或是方法。当然,也不是所有的都适合反射,之前就遇到一个案例,通过反射得到的结果与预期不符。阅读源码发现,经过层层调用后在最终返回结果的地方对应用的权限进行了校验,对于没有权限的应用返回值是没有意义的缺省值,否则返回实际值起到保护用户的隐私目的。

3. RPC调用

java远程调用,服务端反序列化请求数据后,根据全路径类名、方法名、参数列表,解析并反射调用服务端接口对外提供服务。

4. Gateway的实现

为反射调用的扩展实现,不区分客户端实现方式,根据请求信息,反射处理后返回结果。

5. 示例代码

@Service
public class MethodInvokeJob implements SimpleJobProcessor {

    private static final Logger logger = LoggerFactory.getLogger(MethodInvokeJob.class);

    private static List<String> constantKeys =  Arrays.asList("boolean", "char", "int", "byte", "short", "long", "float", "double", "java.lang.String");

    private static List<String> constantValues =  Arrays.asList("java.lang.Boolean", "java.lang.Character", "java.lang.Integer", "java.lang.Byte", "java.lang.Short", "java.lang.Long", "java.lang.Float", "java.lang.Double", "java.lang.String");

    @Autowired
    private ApplicationContext applicationContext;

    @Override
    public void process(JobContext context) throws Exception {
        logger.info("Start Execute Task:" + this.getClass().getSimpleName() + " -> " + context.getParameter());
        Map<String, Object> parameters = (Map<String, Object>) JSON.parse(context.getParameter());
        executeInvoke(parameters);
        logger.info("End Execute Task:" + this.getClass().getSimpleName() + " -> " + context.getParameter());

    }

    /**
     * 执行反射操作,调用指定方法
     *
     * @param parameters
     */
    private void executeInvoke(Map<String, Object> parameters) {

        String className = String.valueOf(parameters.get("className"));
        String method = String.valueOf(parameters.get("methodName"));
        Map<String, Object> params = (Map<String, Object>) JSON.parse(String.valueOf(parameters.get("param")));
        try {
            Class<?> aClass = Class.forName(className);
            Object instance = applicationContext.getBean(aClass);
            Method[] methods = Class.forName(className).getDeclaredMethods();

            Object ret = null;

            for (int i = 0; i < methods.length; i++) {
                if (method.equals(methods[i].getName()) && methods[i].getParameterCount() == params.size()) {
                    logger.info("开始执行反射操作: className {}, method {}, params {}", className, method, parameters.get("param"));
                    methods[i].setAccessible(true);
                    if (0 == params.size()){
                        ret = methods[i].invoke(instance);
                    }else {
                        List<Object> paramList = new ArrayList<>();
                        Class<?>[] paramsType = methods[i].getParameterTypes();
                        for (int j = 0; j < params.size(); j++) {
                            Object param = JSON.parseObject(String.valueOf(parameters.get("param" + j)), paramsType[j]);
                            paramList.add(param);
                        }
                        ret = methods[i].invoke(instance, paramList.toArray());
                    }
                }
                logger.info("执行反射操作完成: {}", ret);
            }
        } catch (Exception e) {
            logger.error("执行反射调用失败->{}", e);
        }
    }

    public void invoke(Map<String, Object> parameters) {

        String className = String.valueOf(parameters.get("className"));
        String method = String.valueOf(parameters.get("methodName"));
        List<Object> params = (List<Object>) JSON.parse(String.valueOf(parameters.get("param")));
        try {
            Class<?> aClass = Class.forName(className);
            Object instance = aClass.newInstance();
            Method[] methods = Class.forName(className).getDeclaredMethods();

            Object ret = null;

            for (int i = 0; i < methods.length; i++) {
                if (method.equals(methods[i].getName()) && methods[i].getParameterCount() == params.size()) {
                    logger.info("开始执行反射操作: className {}, method {}, params {}", className, method, parameters.get("param"));
                    methods[i].setAccessible(true);
                    if (0 == params.size()){
                        ret = methods[i].invoke(instance);
                    }else {
                        List<Object> paramList = new ArrayList<>();
                        Class<?>[] paramsType = methods[i].getParameterTypes();
                        for (int j = 0; j < paramsType.length; j++) {
                            if (constantKeys.contains(paramsType[j].getName())){
                                int index = constantKeys.indexOf(paramsType[j].getName());
                                String decodeType = constantValues.get(index);
                                Class<?> clazz = Class.forName(decodeType);
                                paramList.add(clazz.cast(params.get(j)));
                            }else {
                                paramList.add(JSON.parseObject(String.valueOf(params.get(j)), Class.forName(paramsType[j].getName())));
                            }
                        }
                        methods[i].invoke(instance, paramList.toArray());
                    }
                }
                logger.info("执行反射操作完成");
            }
        } catch (Exception e) {
            logger.error("执行反射调用失败->{}", e);
        }
    }
}
<think>好的,我现在要回答用户的问题:如何在Java中使用反射进行方法调用,并且需要提供一个示例。用户提到了反射的invoke方法,我需要结合他们提供的引用内容来构建答案。 首先,我需要回忆Java反射的基本概念。反射允许程序在运行时访问、检测和修改自身的结构和行为。Method类是反射API的一部分,用于表示类的方法。用户主要关注的是如何通过反射调用方法,特别是invoke方法的使用。 根据引用[1],Method的invoke方法提供了动态调用类方法的能力,这对框架开发、单元测试等场景非常有用。引用[3]和[4]提到了具体的代码示例和注意事项,比如处理私有方法时需要setAccessible(true)。引用[2]则是一个总结,强调反射API的重要性。 接下来,我需要构建一个清晰的步骤说明。首先获取类的Class对象,然后获取Method实例。对于私有方法,需要设置访问权限。然后使用invoke方法调用,传入对象实例和参数。此外,处理静态方法时,实例参数应为null。 需要注意的是,异常处理也很重要,invoke可能会抛出异常,如IllegalAccessException或InvocationTargetException,必须捕获或抛出。 然后,我需要提供一个完整的示例代码,演示如何调用实例方法和静态方法,包括私有方法的处理。代码要包括必要的异常处理,并添加注释解释每一步。 最后,根据用户的要求生成相关问题。问题应涵盖反射的其他应用、优缺点、异常处理和安全问题,以及替代方案。 需要确保回答结构清晰,步骤明确,示例正确,并且引用适当的参考文献。同时,要使用中文,避免使用任何Markdown格式,保持口语化,但内容要准确可靠。</think>在Java中,使用反射调用方法需要经过以下步骤。我们以调用实例方法和静态方法为例进行说明,结合代码示例展示关键操作: ### 一、方法调用的核心步骤 1. **获取Class对象** ```java Class<?> clazz = TargetClass.class; // 通过类直接获取 // 或 Class.forName("全限定类名") ``` 2. **获取Method对象** ```java // 获取公共方法(含继承方法) Method publicMethod = clazz.getMethod("methodName", parameterTypes); // 获取私有方法(需设置访问权限) Method privateMethod = clazz.getDeclaredMethod("privateMethod"); privateMethod.setAccessible(true); // 关键步骤[^4] ``` 3. **调用方法** ```java Object instance = clazz.newInstance(); // 实例方法调用 Object result = publicMethod.invoke(instance, args); // 静态方法调用(第一个参数传null) Object staticResult = staticMethod.invoke(null, args); ``` ### 二、完整示例代码 ```java import java.lang.reflect.*; class Calculator { private int add(int a, int b) { return a + b; } public static double sqrt(double value) { return Math.sqrt(value); } } public class ReflectionDemo { public static void main(String[] args) throws Exception { // 调用私有实例方法 Method addMethod = Calculator.class.getDeclaredMethod("add", int.class, int.class); addMethod.setAccessible(true); // 解除私有访问限制 Calculator calc = new Calculator(); int sum = (int) addMethod.invoke(calc, 5, 3); System.out.println("5 + 3 = " + sum); // 输出:8 // 调用静态方法 Method sqrtMethod = Calculator.class.getMethod("sqrt", double.class); double result = (double) sqrtMethod.invoke(null, 25.0); System.out.println("√25 = " + result); // 输出:5.0 // 带注解方法调用示例(参考引用[3]) Method studyMethod = Study.class.getMethod("study"); if(studyMethod.isAnnotationPresent(Work.class)) { Work work = studyMethod.getAnnotation(Work.class); Method[] annotationMethods = work.annotationType().getDeclaredMethods(); // 通过反射读取注解属性值... } } } ``` ### 三、注意事项 1. **异常处理**:必须处理以下异常 - `IllegalAccessException`:访问权限不足 - `InvocationTargetException`:被调用方法本身抛出异常 - `NoSuchMethodException`:方法不存在 2. **性能优化**: - 缓存频繁使用的Method对象 - 优先使用`getMethod()`获取公共方法,`getDeclaredMethod()`获取类声明方法 3. **类型安全**:反射调用会绕过编译时类型检查,需要显式强制转换返回值类型 ### 四、典型应用场景 1. 框架开发(如Spring的依赖注入) 2. 单元测试(访问私有方法进行测试) 3. 动态代理实现(AOP编程) 4. 配置文件驱动的类加载(参考引用[1][^1])
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值