多方法接口回调_啊?Java反射遇到接口
本文适合有点Java反射基础的同学,在Java反射调用方法时遇到接口参数是一件很蛋疼的事情。
在反射调用方法时需要传参数,像传递基本数据类型进去用就完事,传个对象进去怎么整都没关系,因为你在外部有对象的引用,但 如果需要你传递接口参数,而且这个接口类也是你反射得到的,那怎么拿到接口回调的值呢? 下面通过一个例子告诉你咋整。
场景和需求
场景:假设我是提供方A,有个业务方B,提供ID方C。
其中提供ID方C有如下代码来提供ID。
// IdManager类public class IdManager { private String id; private static volatile IdManager mInstance;private IdManager() { }public static IdManager getInstance() { if (mInstance == null) { synchronized (IdManager.class) { if (mInstance == null) { mInstance = new IdManager(); } } } return mInstance; }public void setId(String id) { this.id = id; }// 加锁是防止多线程调用造成多次开启子线程 public synchronized void getId(Callback callback) { if (id == null || id.isEmpty()) { new Thread(new Runnable() { @Override public void run() { // 一些耗时操作... callback.getId(id); } }).start(); } else { callback.getId(id); } }}
// 获取Id的回调接口Callbackpublic interface Callback { void getId(String id);}
需求是:
如果业务方B接入C(也就是存在IdManager类),那么我方需要获得业务方B设置的Id。
如果业务方B没有接入C(不存在IdManager类),那么我就不获取。
基本分析
懂少许反射的同学肯定认为这太简单了,思路如下:
- 使用类的完整路径加载IdManager类
- 找到getInstance()方法获取对象(由于该类采用单例模式,避免反射去创建多个对象)
- 找到getId()方法
- 利用2找到的IdManager对象调用getId()
开始撸代码
public class Main { public static void main(String[] args) { // 模拟接入方B已经传入id IdManager.getInstance().setId("MyId:10086!"); // 我们的代码 try { // 1. 找不到就catch住抛出的异常(对应第二种未接入的情况) Class<?> idManagerClazz = Class.forName("com.grcen.proxy.IdManager"); // 2. 获取Callback类型 Class<?> callbackClazz = Class.forName("com.grcen.proxy.Callback"); // 3. 找到getInstance方法 Method getInstance = idManagerClazz.getMethod("getInstance"); // 4.将获取到的Callback类型用来找到getId方法 Method getId = idManagerClazz.getMethod("getId", callbackClazz); // 执行getInstance方法获取到单例对象(参数1:执行该方法的对象。参数2:该方法的参数) Object instance = getInstance.invoke(null, null); // 利用instance对象执行getId方法获取ID!! // getId.invoke(instance,????) -> ???该方法的参数是接口该怎么传呢?? } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { // 这里默认处理异常(正式代码别这样搞) e.printStackTrace(); } }}
写到上面出现问号的地方就尴尬了,因为在IdManager类中是通过接口参数来回调id值的。那理所当然我们应该传入应该接口参数,但在该场景下并不知道有没有Callback类,是没法声明接口的。即使找到callbackClazz调用newInstance()来创建对象,那请问怎么知道回调结果啊?而且只能实现接口不能实例化接口。
使用代理
代理顾名思义找个中间商实现接口,在实现的方法中即可拿到回调值。在Java中提供了InvocationHandler接口实现代理。InvocationHandler意为调用处理者。目的很明确:找个类实现我反射拿到的接口,在实现的方法中拿到回调的值。
创建MyHandler类实现InvocationHandler接口
public class MyHandler implements InvocationHandler { /** * 参数说明:这些参数先知道是个什么意思 * @param proxy 所代理的那个真实对象 * @param method 我们所要调用真实对象的某个方法的Method对象 * @param args 调用真实对象某个方法时接受的参数 * @return 代理执行完方法所返回的对象 * @throws Throwable 执行过程抛出的各种异常 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 这里什么也不干就打印回调的值 System.out.println(args[0]); return null; }}
利用Proxy类关联Callback接口和MyHandler
// Main类完整代码public class Main { public static void main(String[] args) { // 模拟接入方已经传入id IdManager.getInstance().setId("MyId:10086!"); // 我们的代码 try { Class<?> idManagerClazz = Class.forName("com.grcen.proxy.IdManager"); Class<?> callbackClazz = Class.forName("com.grcen.proxy.Callback"); Method getInstance = idManagerClazz.getMethod("getInstance"); Method getId = idManagerClazz.getMethod("getId", callbackClazz); Object instance = getInstance.invoke(null, null); // 新增代码 MyHandler myHandler = new MyHandler(); // 参数说明在下文 Object myCallback = Proxy.newProxyInstance( Main.class.getClassLoader(), new Class[]{callbackClazz}, myHandler); getId.invoke(instance, myCallback); } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } }}
Proxy.newProxyInstance()的参数有:(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
- 类加载器(不懂的可以看看《深入理解JVM虚拟机》)
- 一个Interface类数组,表示要给代理对象实现的接口有哪些
- 表示的是当我这个动态代理对象在调用方法的时候,会调用到哪个InvocationHandler的invoke方法。(即将该代理对象与MyHandler关联)
先来看看代码运行结果:
再梳理下主要过程,先从getId.invoke(instance,myCallback)来看,执行getId方法,传入执行对象instance,方法所需参数myCallback。然后在getId方法中回调接口,因为myCallback是个代理,它的接口实现在MyHandler,所以最后回调执行的是MyHandler中的invoke方法。前面大致罗列了invoke方法的参数意思,我们来验证一下。
// MyHandlerpublic class MyHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("invoke参数2真实代理对象所调用的方法" + method); System.out.println("invoke参数3调用方法的传入参数为:" + args[0]); // 此处已知只有一个参数 return null; }}
结果:
注意:
- 此处我没有将proxy打印出来是因为会出现栈溢出。重申一遍现在的逻辑是 找proxy对象的方法就会到MyHandler中的invoke寻找,那我在invoke中调用proxy,就会出现死循环,方法栈一直入栈。不懂方法栈可以看看《深入JVM虚拟机》
- 还记得前面Proxy.newProxyInstance()的参数的参数2吗?可以传入一组Interface类,所以当给代理类实现多个接口是需要利用invoke的参数2做区分
BTW:其实这种动态代理的实现大多数情况下是用在AOP编程中的。
原文链接:
https://blog.csdn.net/qq_40883985/article/details/96633773
多方法接口回调_啊?Java反射遇到接口相关推荐
- java接口课程_用java定义一个接口,用于查询课程
定义一个类Stu,包括如下属性:学号.姓名.性别.专业.课程,实现以下方法:每个属性的获取和定义,要求至少包含一个构造函数.定义一个接口类,定义方法qcc()用来查询课程.编写一... 定义一个类 S ...
- 多方法接口回调_浅析接口定义和接口回调
目录 一.接口定义 二.接口的特点 三.使用接口的好处 四.接口回调 五.Android中的接口回调机制 一.接口定义 接口通常以interface来修饰. public interface Anim ...
- java怎么学好 用好接口_Java公开课|Java的实现接口怎么用,这才是学习好Java必备的实用函数,你会吗...
[摘要]作为一门面向对象编程语言,Java吸收了C++语言的优点,也展现了其强大的一面,我们能在各个地方看到其功能强大和简单易用的两个特征,当然,也吸引了很多程序员的注意力,所以就有人想了解Java的 ...
- java invoke 返回类型_解析Java反射 - invoke方法
最近工作中涉及到获取同程火车票,大概描述为:将本地获取的发出城市,目的城市及出发时间按固定格式封装,调用接口获取可乘坐座席等级最高的火车票,接口返回数据用包含三层类封装的类接受,接受的类总共为四层,倒 ...
- java方法的调用类型转换_关于java:实现接口,方法调用和类型转换的方法
考虑以下代码 interface MyInterface{ void method(String s);// if we write static modifier we have compile e ...
- c 与java 反射性能_谈谈Java 反射的快慢
[相关学习推荐:java基础教程] 反射到底是好是坏 说到Java 中的反射,初学者在刚刚接触到反射的各种高级特性时,往往表示十分兴奋,甚至会在一些不需要使用反射的场景中强行使用反射来「炫技」.而经验 ...
- java初反射_初始 java 反射机制 (一)
反射机制详解 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为j ...
- c++ 异步回调_知道Java中的回调机制吗?
调用和回调机制 在一个应用系统中, 无论使用何种语言开发, 必然存在模块之间的调用, 调用的方式分为几种: 1.同步调用 同步调用是最基本并且最简单的一种调用方式, 类A的方法a()调用类B的方法b( ...
- java 对象复制 反射_利用Java反射机制实现对象相同字段的复制操作
一.如何实现不同类型对象之间的复制问题? 1.为什么会有这个问题? 近来在进行一个项目开发的时候,为了隐藏后端数据库表结构.同时也为了配合给前端一个更友好的API接口文档(swagger API文档) ...
最新文章
- landmark如何恢复oracle,LandmarkR5000数据迁移方法及迁移常见问题(带图解)
- 【深度学习】利用一些API进行图像数据增广
- java 环境配置 mac_Java:配置环境(Mac)——JDK
- Navicat怎样同步两个数据库中的表
- SAP云平台上的502 Bad Gateway错误
- C#验证IP是否为局域网地址的三种方法
- ERP项目实施记录02
- Ubuntu 12.04下安装OpenCV 2.3.1,图像二值化
- AGC022E Median Replace
- 即时通讯 音视频 开发技术
- BT.1120协议简介
- 最新黑马内部视频+相关配套学习资料
- MapReduce----并行支持向量机(PSVM)第二部分之原始对偶内点法
- PADS(4)——PADS Logic原理图设计技巧
- Excel批量自动删除空白行
- 美计算机科学学者建议:加强青少年网络安全意识引导
- 1.1 迷茫的大学——《逆袭大学》连载
- hive No matching method for class hiveUDF.PhoneTM with (string, int, int)
- PKI CA RA KMC
- 基于Arduino锂电池容量测试仪
热门文章
- jdk8 接口抽象类区别_JDK 8时代的抽象类与接口
- java 字符串对齐_最佳字符串对齐的Java实现
- 使用IntelliJ调试Java流
- java方法带参数返回值_Java方法中的参数太多,第6部分:方法返回
- apache camel_轻量级的开源集成:Apache Camel还是Spring集成?
- 回调函数中有回调函数吗_嗨,那里有回调!
- java方法传对象参数_Java方法中的参数太多,第2部分:参数对象
- json绑定到实体_绑定到JSON和XML –处理集合
- spring 事件模型_Spring–设计领域模型和服务层
- jmc线程转储_如何分析线程转储– IBM VM