多方法接口回调_啊?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类),那么我就不获取。

基本分析

懂少许反射的同学肯定认为这太简单了,思路如下:

  1. 使用类的完整路径加载IdManager类
  2. 找到getInstance()方法获取对象(由于该类采用单例模式,避免反射去创建多个对象)
  3. 找到getId()方法
  4. 利用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)

  1. 类加载器(不懂的可以看看《深入理解JVM虚拟机》)
  2. 一个Interface类数组,表示要给代理对象实现的接口有哪些
  3. 表示的是当我这个动态代理对象在调用方法的时候,会调用到哪个InvocationHandler的invoke方法。(即将该代理对象与MyHandler关联)

先来看看代码运行结果:

d970865607cd1ca77f4e3378b1f589d4.png

再梳理下主要过程,先从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;    }}

结果:

1e71aeac27971457a47f343151105b0a.png

注意:

  1. 此处我没有将proxy打印出来是因为会出现栈溢出。重申一遍现在的逻辑是 找proxy对象的方法就会到MyHandler中的invoke寻找,那我在invoke中调用proxy,就会出现死循环,方法栈一直入栈。不懂方法栈可以看看《深入JVM虚拟机》
  2. 还记得前面Proxy.newProxyInstance()的参数的参数2吗?可以传入一组Interface类,所以当给代理类实现多个接口是需要利用invoke的参数2做区分
BTW:其实这种动态代理的实现大多数情况下是用在AOP编程中的。

原文链接:

https://blog.csdn.net/qq_40883985/article/details/96633773

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/336601.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

0与1世界的初级编程篇之C语言

C语言是一门面向过程的计算机编程语言&#xff0c;与C、Java等面向对象编程语言有所不同。C语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、仅产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。C语言描述问题比汇编语言迅速&#xff0c;工作量小…

QT 手动创建信号函数 与 槽函数

自定义信号槽必须要有Q_OBJCT 自己通过关键字signals创建信号函数,只声明信号函数即可&#xff0c;系统会自动定义它。 利用 public slots: 声明一个槽函数&#xff0c;槽函数不会自动生成&#xff0c;并且去定义它。

九九乘法表代码口述_利用随机函数实现座次表的随机排座

昨天听完讲座&#xff0c;晚上忍不住写了份学后感&#xff0c;今天有老师在文章下留言问如何实现随机排座&#xff0c;上午在快速理赔中心处理交通事故&#xff0c;处理完后将车开去4S店维修&#xff0c;回来后给娃做完中饭就开始写代码实现这个功能&#xff0c;因为有C功底&am…

jep使用_JEP 277“增强弃用”非常好。 但这是一个更好的选择

jep使用维护API很难。 我们正在维护非常复杂的jOOQ API。 但是就语义版本而言&#xff0c;我们遵循相对宽松的规则 。 当您阅读Brian Goetz和其他人关于在JDK中保持向后兼容性的评论时&#xff0c;我只能对他们的工作表示敬意。 显然&#xff0c;我们都希望最终移除Vector &a…

古巴比伦乘法_古巴平台中的通用过滤器–类固醇上的excel过滤器

古巴比伦乘法正如我上次承诺的那样&#xff0c;我计划浏览该平台的某些功能&#xff0c;这些功能我认为非常有价值。 所以我将在这里做一些系列。 从明显的用户界面&#xff0c;过滤&#xff0c;安全性到一些高级功能&#xff08;如Web Portal&#xff0c;可扩展性&#xff0c;…

excel运行没反应_Excel数据很少文件却很大,问题出在哪里呢?两种方法轻松解决...

经常和Excel打交道的小伙伴可能会有这样的困惑&#xff0c;一个Excel工作簿中的数据明明很少&#xff0c;文件所占的空间却很大。打开这种Excel工作簿后&#xff0c;电脑CPU占用率瞬间飙升&#xff0c;甚至遇见工作簿没有响应的情况出现。遇见这样的工作簿让人窝火&#xff0c;…

java lambda::_基准测试:Java 8 Lambda和流如何使您的代码慢5倍

java lambda::与长期的实现相比&#xff0c;Java 8 lambda和流的性能如何&#xff1f; Lambda表达式和流在Java 8中受到了热烈的欢迎。这些是迄今为止很激动人心的功能&#xff0c;很长一段时间以来&#xff0c;它们就已经应用到Java中了。 新的语言功能使我们可以在代码中采用…

如何在java中实现小数点自增_java编个计算器怎么在加入小数点

展开全部我做的可以运行&#xff0c;你看看吧&#xff01;import java.awt.*;import java.awt.event.*;import java.lang.*;import javax.swing.*;public class Counter extends Frame{//声明三个面板的布局GridLayout gl1,gl2,gl3;Panel p0,p1,p2,p3;JTextField tf1;TextField…

在Java中使用FileChannel和ByteBuffer对文件进行读写

过去&#xff0c;我讨论过RandomAccessFile以及如何将其用于在Java中进行更快的IO&#xff0c;在本Java NIO教程中&#xff0c;我们将了解如何通过使用FileChannel和ByteBuffer来使用读/写数据。 Channel提供了一种从文件读取数据的替代方法&#xff0c;它提供了比InputStream…

tp5防止sql注入mysql_PHP+Mysql防止SQL注入的方法(life)

这篇文章介绍的内容是关于PHPMysql防止SQL注入的方法&#xff0c;有着一定的参考价值&#xff0c;现在分享给大家&#xff0c;有需要的朋友可以参考一下我的官方群点击此处。方法一&#xff1a;mysql_real_escape_string -- 转义 SQL 语句中使用的字符串中的特殊字符&#xff0…

neo4j 关系属性_Neo4j:特定关系与一般关系+属性

neo4j 关系属性为了在Neo4j查询中获得最佳的遍历速度&#xff0c;我们应该使关系类型尽可能具体 。 让我们看一下几周前我在Skillsmatter上发表的“ 建模建议引擎建模 ”演讲中的一个例子。 我需要决定如何为成员和事件之间的“ RSVP”关系建模。 一个人可以对某个事件表示“…

2008r2配置 iis mysql php_Windows 2008 R2服务器配置文档iis+php+mysql

关闭防火墙关闭防火墙打开桌面远程连接修复系统依赖文件IIS配置添加角色创建一个网站验证iis是否成功删除默认创建的网站添加网站PHP配置安装解压&#xff0c;改名创建一个php.ini&#xff0c;php.ini由php.ini-production改名得到如果输入php -v后出现如下报错解决方法如下(安…

接口入口在什么地方_弱电工程施工图审查要点?有哪些地方需要审核?审核要求是什么?...

前言&#xff1a;弱电工程施工图审核标准是什么呢&#xff1f;需要审核哪些方面呢&#xff1f;有没有可以参考的地方&#xff1f;今天就分享一套弱电系统施工图审核技术要求&#xff0c;可以参考正文&#xff1a;1.设计文件设计文件是否完整(包括设计说明、平面图、系统图(单体…

drill apache_Apache Drill 1.4性能增强的简要概述

drill apache今天&#xff0c;我们很高兴地宣布&#xff0c;MapR发行版中现已提供Apache Drill 1.4。 钻1.4是MAPR生产就绪和支持的版本&#xff0c;可以从下载这里 &#xff0c;找到1.4版本说明这里 。 Drill 1.4以其高度灵活和可扩展的体系结构为基础&#xff0c;带来了多种…

docker java mysql_Docker 搭建 MySQL 服务

安装 Docker请参考我的另一篇文章建立镜像拉取镜像# 拉取最新版本镜像docker pull mysql# 拉取执行版本镜像docker pull mysql:版本号检查拉取是否成功docker images创建数据库容器(不建立数据映射)docker run -d --name mysql --rm -p 3306:3306 -e MYSQL_ROOT_PASSWORD123456…

php cdi_Swing应用程序中的CDI事件将UI和事件处理分离

php cdi在愉快地围绕CDI构建我的代码几年之后&#xff0c;使用它根据众所周知的模式来构造我的代码非常自然。 CDI是一种旨在在Java EE应用程序服务器中使用的依赖项注入机制&#xff0c;这可能被视为不利。 但是&#xff0c;我想证明它可以在Java SE应用程序中使用并且具有巨大…

java关闭窗口函数_2016年将是Java终于拥有窗口函数的那一年!

java关闭窗口函数你没听错。 到目前为止&#xff0c;出色的窗口功能是SQL独有的功能。 甚至复杂的函数式编程语言似乎仍然缺少这种漂亮的功能&#xff08;如果我错了&#xff0c;请纠正我&#xff0c;Haskell伙计们&#xff09;。 我们撰写了许多有关窗口函数的博客文章&#…

jvm gc阻塞时长 占比_jvm进行转义分析需要多长时间? 可能比您想象的要长。

jvm gc阻塞时长 占比这篇文章着眼于转义分析&#xff0c;特别是jvm在运行的程序中执行转义分析需要多长时间。 我做了一些观察&#xff0c;但目前还没有全部解释。 作为介绍&#xff0c;让我们绕道看看jvm -Xcomp中一个鲜为人知且使用更少的标志&#xff08;我们将看到这是一件…

jquery mysql php_jQuery+PHP+Mysql在线拍照和在线浏览照片

本文示例建立在本站helloweba.net两篇文章之上&#xff0c;一篇是用于在线拍照的&#xff1a;JavascriptPHP实现在线拍照功能&#xff0c;另一篇是用于浏览照片的&#xff1a;Fancybox丰富的弹出层效果。如果您对在线拍照和Fancybox不大了解&#xff0c;可以先参照以上两篇文章…

设计散列表实现通讯录查找系统_[源码和文档分享]利用哈希表实现电话号码查询系统...

第一章 需求分析1.1 问题描述设计一个电话号码查询系统&#xff0c;为来访的客⼈提供各种信息查询服务。1.2 基本要求设计每个记录有下列数据项&#xff1a;电话号码、用户名、地址从键盘输入个记录&#xff0c;分别以电话号码和用户名为关键字建立不同散列表存储采用一定的方法…