沧州网站建设 网络服务,网上做任务网站有哪些内容,黄冈网站推广,安卓app制作工具本文适合有点Java反射基础的同学#xff0c;在Java反射调用方法时遇到接口参数是一件很蛋疼的事情。在反射调用方法时需要传参数#xff0c;像传递基本数据类型进去用就完事#xff0c;传个对象进去怎么整都没关系#xff0c;因为你在外部有对象的引用#xff0c;但 如果需…本文适合有点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