JDK动态代理中如何执行代理接口中的default方法?

写在前面

试想一下,有很多场景下,我们有特殊要求,希望在JDK动态代理的接口中希望有自己自定义的逻辑,而后再执行代理方法。譬如:

interface ProxyInterface {Object send(Object msg);default Object checkThenSend(Object msg) {// 此处可以检查 msg,以及一些默认赋值msg.setProps(xxx);// 再调用执行方法return send(msg);}
}

抽象方法send()的参数 msg,我们希望设置一些公共的参数,但是在每一处调用时都设置一次,就显得重复啰嗦。因此,在checkThenSend()中就可以统一设置值,然后再调用 send()方法。

本文就是如何实现从而达到目的。

实现原理

jdk8中如果直接调用MethodHandles#lookup()获取到的link MethodHandles.Lookup
在调用方法MethodHandles.Lookup#findSpecial(java.lang.Class, java.lang.String, java.lang.invoke.MethodType, java.lang.Class)
和 link MethodHandles.Lookup#unreflectSpecial(java.lang.reflect.Method, java.lang.Class)获取父类方法句柄MethodHandle}时,
可能出现权限不够, 抛出如下异常, 所以通过反射创建{@link MethodHandles.Lookup}解决该问题.
java.lang.IllegalAccessException: no private access for invokespecial:
而jdk11中直接调用MethodHandles#lookup()获取到的MethodHandles.Lookup,
也只能对接口类型才会权限获取方法的方法句柄MethodHandle.
如果是普通类型Class,需要使用jdk9开始提供的MethodHandles#privateLookupIn(java.lang.Class, java.lang.invoke.MethodHandles.Lookup)方法。

工具

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;public final class MethodHandlesUtil {private static final Logger logger = LoggerFactory.getLogger(MethodHandlesUtil.class);private static final int ALLOWED_MODES = MethodHandles.Lookup.PRIVATE| MethodHandles.Lookup.PROTECTED| MethodHandles.Lookup.PACKAGE| MethodHandles.Lookup.PUBLIC;private static Constructor<MethodHandles.Lookup> java8LookupConstructor;private static Method privateLookupInMethod;static {//先查询jdk9 开始提供的java.lang.invoke.MethodHandles.privateLookupIn方法,//如果没有说明是jdk8的版本.(不考虑jdk8以下版本)try {privateLookupInMethod = MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class);} catch (NoSuchMethodException e) {privateLookupInMethod = null;logger.info("There is no [java.lang.invoke.MethodHandles.privateLookupIn(Class, Lookup)] method in this version of JDK");}//jdk8//这种方式其实也适用于jdk9及以上的版本,但是上面优先,可以避免 jdk9 反射警告if (privateLookupInMethod == null) {try {java8LookupConstructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);java8LookupConstructor.setAccessible(true);} catch (NoSuchMethodException e) {//可能是jdk8 以下版本throw new IllegalStateException("There is neither 'privateLookupIn(Class, Lookup)' nor 'Lookup(Class, int)' method in java.lang.invoke.MethodHandles.",e);}}}/*** java9中的MethodHandles.lookup()方法返回的Lookup对象* 有权限访问specialCaller != lookupClass()的类* 但是只能适用于接口, {@link java.lang.invoke.MethodHandles.Lookup#checkSpecialCaller}*/public static MethodHandles.Lookup lookup(Class<?> callerClass) {//使用反射,因为当前jdk可能不是java9或以上版本if (privateLookupInMethod != null) {try {return (MethodHandles.Lookup) privateLookupInMethod.invoke(MethodHandles.class, callerClass, MethodHandles.lookup());} catch (IllegalAccessException | InvocationTargetException e) {throw new RuntimeException(e);}}//jdk8try {return java8LookupConstructor.newInstance(callerClass, ALLOWED_MODES);} catch (Exception e) {throw new IllegalStateException("no 'Lookup(Class, int)' method in java.lang.invoke.MethodHandles.", e);}}public static MethodHandle getSpecialMethodHandle(Method parentMethod) {final Class<?> declaringClass = parentMethod.getDeclaringClass();MethodHandles.Lookup lookup = lookup(declaringClass);try {return lookup.unreflectSpecial(parentMethod, declaringClass);} catch (IllegalAccessException e) {throw new RuntimeException(e);}}
}

应用

以下是在JDK动态代理中如何执行代理接口中的default方法。

import java.lang.invoke.MethodHandle;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class ProxyInvocationHandler<T> implements InvocationHandler {private Class<T> interfaceClass;private final static Map<Method, MethodHandle> DEFAULT_METHOD_HANDLE = new ConcurrentHashMap<>();public T bind(Class<T> interfaceClass) {this.interfaceClass = interfaceClass;return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, this);}/*** 处理代理实例上的方法调用并返回结果。* 当在与其关联的代理实例上调用方法时,将在调用处理程序上调用此方法*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {final Class<T> iCls = this.interfaceClass;// 如果是默认方法,需要获取方法句柄才能执行,原因是无法走代理if (method.isDefault()) {return DEFAULT_METHOD_HANDLE.computeIfAbsent(method, m -> MethodHandlesUtil.getSpecialMethodHandle(method).bindTo(proxy)).invokeWithArguments(args);}// 正常执行其他逻辑return args;}
}

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

ssm113ssm框架的购物网站+vue(论文+源码)_kaic

毕 业 设 计&#xff08;论 文&#xff09; 题目&#xff1a;网上超市系统设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本网上超市系统就是在这…

C++ 里面散发的咖喱味儿 - Currying函数式编程

C 里面散发的咖喱味儿 - Currying函数式编程 大家好&#xff0c;最近几篇都在聊C里面的函数式编程&#xff0c;今天我们继续就某一个点来深入聊一下&#xff0c;来聊聊在 C 中如何使用 std::bind 来实现函数式编程&#xff0c;尤其是柯里化&#xff08;Currying&#xff09;这…

【Gitee版】一篇教你如何快速入门git(详解)

前言--区分Git与Gitee Git 是一个强大的分布式版本控制系统&#xff0c;用于管理源代码。市面上有很多基于git的仓库网站&#xff0c;例如&#xff1a;GitHub、Gitee、GitCode等&#xff0c;它们之间的关系就好像是&#xff1a;git为基类&#xff0c;剩余为子类的样子。使用的…

Linux系统编程学习 NO.11——进程的概念(2)

谈谈进程的性质 进程的竞争性 由于CPU资源是稀缺的,进程数量是众多的。不可避免需要造成进程排队等待CPU资源的动作&#xff0c;内核的设计者为了让操作系统合理的去调度这这些进程&#xff0c;就产生了进程优先级的概念。设置合理的进程优先级能让不同进程公平的去竞争CPU资…

灵神 刷题DAY1

Python与java的刷题的区别 1. Python没有分号 2. Python不能return的时候赋值 3. Python没有小括号和花括号 4. Python的循环很奇怪&#xff0c;没有for(int i0;i<32;i)这种形式 而是直接用的是for i in range(n)这种 5. Python中没有 6. Python中没有&& 是an…

Nginx中使用keepalive实现保持上游长连接实现提高吞吐量示例与测试

场景 HTTP1 .1之后协议支持持久连接&#xff0c;也就是长连接&#xff0c;优点在于在一个TCP连接上可以传送多个HTTP请求和响应&#xff0c; 减少了建立和关闭连接的消耗和延迟。 如果我们使用了nginx去作为反向代理或者负载均衡&#xff0c;从客户端过来的长连接请求就会被…

【Spring AOP 原理】

首先AOP跟OOP(面向对象编程)、IOC(控制反转)一样都是一种编程思想 跟OOP不同, AOP是面向切面编程, 面对多个不具备继承关系的对象同时需要引入一段公共逻辑的时候, OOP就显得有点笨重了, 而AOP就游刃有余, 一个切面可以横跨多个类或者对象去执行公共逻辑, 极大的提升了开发效率…

Vue3集成搜索引擎智能提示API

需求&#xff1a; 如何在项目中实现像百度搜索框一样的智能提示效果&#xff0c;如下图所示&#xff1a; 相关知识&#xff1a; 下面是各厂商提供的免费API 厂商请求百度http://suggestion.baidu.com/su?wd中国&cbwindow.baidu.sug必应http://api.bing.com/qsonhs.as…

python3的基本数据类型:可变集合的用法

一. 简介 前面学习了 python3中的一种基本数据类型-集合&#xff0c;文章如下&#xff1a; python3的基本数据类型&#xff1a;集合的创建与分类-CSDN博客 本文继续学习 Python3中的集合&#xff0c;主要学习 可变集合的用法。 二. python3的基本类型&#xff1a;可变集合的…

从零开始:我的鸿蒙学习之旅(二)

前言 记录我在学习鸿蒙操作系统过程中的成长&#xff0c;旨在激励我自己&#xff0c;也希望能激发读者们的学习热情&#xff0c;一起愉快地探索鸿蒙开发的世界&#xff01; 我说说这几天的学习成果吧&#xff0c;将开发入门的第一部分的剩下小节以及第二部分的第一小结写完了…

SSM学习记录(一)之SSM整合

SSM学习记录&#xff08;一&#xff09;之SSM整合 一、SSM整合二、SSM整合的核心问题1、SSM需要几个IoC容器2、每个IoC容器对应哪些类型组件3、IoC容器之间的关系和调用方向4、具体有多少配置以及对应的容器的关系5、IoC初始化方式和配置位置 一、SSM整合 微观&#xff1a;将学…

【从理论到应用】HTTP请求响应详解 (请求数据格式,请求方式,Web开发中的体现)

目录 一.HTTP协议 二.HTTP请求数据格式 请求方式 三.Web开发中的HTTP请求与响应 接收HTTP请求 同一响应格式 四.使用第三方工具发送HTTP请求&#xff08;Apifox、postman、Yapi&#xff09; 一.HTTP协议 HTTP&#xff08;Hypertext Transfer Protocol&#xff0c;超…

猎板PCB罗杰斯板材的应用案例

以下是几个猎板 PCB 与罗杰斯板材结合的具体案例&#xff1a; 案例一&#xff1a;5G 通信基站天线 PCB 在 5G 通信基站的天线系统中&#xff0c;对高频信号的传输和处理要求极高。猎板 PCB 采用罗杰斯板材&#xff0c;凭借其稳定的低介电常数&#xff08;如 RO4003C 板材&…

基于Java Springboot快递物流管理系统

一、作品包含 源码数据库全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Layui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA 数据库&#xff1a;MySQL8.0 数据库管…

力扣662:二叉树的最大宽度

给你一棵二叉树的根节点 root &#xff0c;返回树的 最大宽度 。 树的 最大宽度 是所有层中最大的 宽度 。 每一层的 宽度 被定义为该层最左和最右的非空节点&#xff08;即&#xff0c;两个端点&#xff09;之间的长度。将这个二叉树视作与满二叉树结构相同&#xff0c;两端…

Servlet的使用

一.Servelt简介 1.为什么需要servlet:因为前端三件套无法操控数据库,即与用户进行交互操作 2.servlet由服务器端调用和执行的(由tomcat解析和调用的),由java语言编写,本质就是java类 3.功能强大,可以完成几乎所有的网站功能,按照Servlet规范开发 二.手动开发Servelt 1.Servl…

【嵌入式C语言】GCC概述+C语言编译过程

目录 前言1 课程介绍1.1 计算机程序语言的学习思路?1.2 基本程序设计思想:1.3 C语言工具的特性:1.4 推荐教材 2 GCC的使用及其常用选项介绍2.1 GCC概述gcc -vgcc -ogcc -v -o 2.2 C语言编译过程2.2.1 预处理2.2.2 编译2.2.3 汇编2.2.4 链接2.2.5 问题 2.3 宏的使用 前言 重新学…

C语言 数组排序 – 插入法排序 - C语言零基础入门教程

目录 一.简介二.数组插入法排序原理三.数组插入法排序实战四.猜你喜欢 零基础 C/C 学习路线推荐 : C/C 学习目录 >> C 语言基础入门 一.简介 经过前面的学习&#xff0c;我们已经学会了数组遍历&#xff0c;在开发中&#xff0c;我们经常回碰到对数组进行排序&#xff0c…

vulnhub- Machine_Matrix_v3靶机的测试报告

目录 一、测试环境 1、系统环境 2、使用工具/软件 二、测试目的 三、操作过程 1、信息搜集 2、Getshell 3、提权 四、结论 一、测试环境 1、系统环境 渗透机&#xff1a;kali2021.1(192.168.200.131) 靶 机&#xff1a;Linux matrix 4.16.3-porteus(192.168.200.1…