Java动态代理之InvocationHandler最简单的入门教程(注解版)

2024-06-08 11:38

本文主要是介绍Java动态代理之InvocationHandler最简单的入门教程(注解版),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 

网上关于Java的动态代理,Proxy和InvocationHandler这些概念有讲解得非常高深的文章。其实这些概念没有那么复杂。现在咱们通过一个最简单的例子认识什么是InvocationHandler。值得一提的是,InvocationHandler在Spring框架实现中被广泛使用,这意味着我们吃透了InvocationHandler,就为将来的Spring源码学习打下一个坚实的基础。

开发一个接口,包含两个方法,可以向指定的人问候“你好”或者“再见”。

public interface IHello {void sayHello(String name);void sayGoogBye(String name);}

创建一个简单的类,实现这个IHello接口。

public class Helloimplements implements IHello {@Overridepublic void sayHello(String name) {System.out.println("Hello " + name);}@Overridepublic void sayGoogBye(String name) {System.out.println(name+" GoodBye!");}
}

消费这个实现类,迄今为止没什么特别的。

现在假设我们接到了这个需求:老板要求在该实现类每次问候某人时,必须把问候的细节记录到日志文件里。为了简单起见,我们在问候前打印下面的一行语句来模拟日志记录的动作。

System.out.println("问候之前的日志记录...");

您也许会说,这还不简单?直接修改Helloimplements的对应方法,把这行日志插入到对应方法即可。

然而,老板的要求是:不允许你修改原来的Helloimplements类。在现实场景中,Helloimplements可能是第三方的jar包提供的,我们没有办法修改代码。

您也许会说,我们可以用设计模式里的代理模式,即创建一个新的Java类作为代理类,同样实现IHello接口,然后将Helloimplements类的实例传入代理类。我们虽然被要求不允许修改Helloimplements的代码,但是可以把日志记录代码写在代理类里。完整代码如下:

public class StaticProxy implements IHello {private IHello iHello;public void setImpl(IHello impl){this.iHello = impl;}@Overridepublic void sayHello(String name) {System.out.println("问候之前的日志记录...");iHello.sayHello(name);}@Overridepublic void sayGoogBye(String name) {System.out.println("问候之前的日志记录...");iHello.sayGoogBye(name);}static public void main(String[] arg) {Helloimplements hello = new Helloimplements();StaticProxy proxy = new StaticProxy();proxy.setImpl(hello);proxy.sayHello("Jerry");}}

这种做法能够实现需求:

下面我们再看如何用InvocationHandler实现同样的效果。

DynaProxyHello的作用就是实现InvocationHandler接口实现代理功能,并在DynaProxyHello方法的invoke中去实现自己需要的业务功能

InvocationHandler是一个JDK提供的标准接口。看下面的代码:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DynaProxyHello implements InvocationHandler {private Object delegate;public Object bind(Object delegate) {this.delegate = delegate;return Proxy.newProxyInstance(this.delegate.getClass().getClassLoader(), this.delegate.getClass().getInterfaces(), this);}public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {Object result = null;try {System.out.println("问候之前的日志记录...");// JVM通过这条语句执行原来的方法(反射机制)result = method.invoke(this.delegate, args);}catch (Exception e) {e.printStackTrace();}return result;}

上面代码里的bind方法很想我之前代理类StaticProxy的setImpl方法,只不过这个bind方法的输入参数类型更加通用。日志记录的代码写在方法invoke里。

看看如何使用:

static public void main(String[] arg) {//DynaProxyHello的作用就是实现InvocationHandler接口实现代理功能,并在DynaProxyHello方法的invoke中去实现自己需要的业务功能DynaProxyHello helloproxy = new DynaProxyHello();Helloimplements hello = new Helloimplements();//将hello设置进 代理类中,实现在此作用域中Helloimplements实例hello的所有调用都会经过DynaProxyHello代理转发IHello ihello = (IHello) helloproxy.bind(hello);ihello.sayHello("Jerry");
}

执行效果和StaticProxy那种解决方案完全一致。

咱们先来调试一下。当bind方法执行时,方法Proxy.newProxyInstance被调用,Helloimplements类的实例被传入。

我们在调试器里观察IHello ihello = (IHello) helloproxy.bind(hello)这行语句返回的ihello变量。虽然它的静态类型是IHello,但请注意,在调试器里观察它的实际类型,并不是Helloimplements的实例,而是JVM给我们加过工的,包含了我们在invoke方法里手写的那行日志记录代码。这个ihello类型为$Proxy0。

 

调用bind方法获得的接口实例,已经是被代理对象实例了,因此调用sayHello方法,自动转交

当这个被JVM加过工的变量的sayHello方法被调用时,JVM自动将调用转交到DynaProxyHello.invoke去:

于是,在invoke方法里,我们手写的日志记录代码被执行,然后通过Java反射执行原始的sayHello代码。

有的朋友可能会问,你这个InvocationHandler看起来比静态代理StaticProxy还复杂啊?有什么好处?

假设老板的需求又变了,在调用问候和说再见的方法里,要使用不同的日志记录策略。

看看用InvocationHandler如何优雅实现吧:

希望这个例子能让大家对Java的动态代理之InvocationHandler有了最基本的了解。

这篇关于Java动态代理之InvocationHandler最简单的入门教程(注解版)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1042076

相关文章

Java Predicate接口定义详解

《JavaPredicate接口定义详解》Predicate是Java中的一个函数式接口,它代表一个判断逻辑,接收一个输入参数,返回一个布尔值,:本文主要介绍JavaPredicate接口的定义... 目录Java Predicate接口Java lamda表达式 Predicate<T>、BiFuncti

Spring Security基于数据库的ABAC属性权限模型实战开发教程

《SpringSecurity基于数据库的ABAC属性权限模型实战开发教程》:本文主要介绍SpringSecurity基于数据库的ABAC属性权限模型实战开发教程,本文给大家介绍的非常详细,对大... 目录1. 前言2. 权限决策依据RBACABAC综合对比3. 数据库表结构说明4. 实战开始5. MyBA

Spring Security方法级安全控制@PreAuthorize注解的灵活运用小结

《SpringSecurity方法级安全控制@PreAuthorize注解的灵活运用小结》本文将带着大家讲解@PreAuthorize注解的核心原理、SpEL表达式机制,并通过的示例代码演示如... 目录1. 前言2. @PreAuthorize 注解简介3. @PreAuthorize 核心原理解析拦截与

一文详解JavaScript中的fetch方法

《一文详解JavaScript中的fetch方法》fetch函数是一个用于在JavaScript中执行HTTP请求的现代API,它提供了一种更简洁、更强大的方式来处理网络请求,:本文主要介绍Jav... 目录前言什么是 fetch 方法基本语法简单的 GET 请求示例代码解释发送 POST 请求示例代码解释

Java图片压缩三种高效压缩方案详细解析

《Java图片压缩三种高效压缩方案详细解析》图片压缩通常涉及减少图片的尺寸缩放、调整图片的质量(针对JPEG、PNG等)、使用特定的算法来减少图片的数据量等,:本文主要介绍Java图片压缩三种高效... 目录一、基于OpenCV的智能尺寸压缩技术亮点:适用场景:二、JPEG质量参数压缩关键技术:压缩效果对比

Java调用C++动态库超详细步骤讲解(附源码)

《Java调用C++动态库超详细步骤讲解(附源码)》C语言因其高效和接近硬件的特性,时常会被用在性能要求较高或者需要直接操作硬件的场合,:本文主要介绍Java调用C++动态库的相关资料,文中通过代... 目录一、直接调用C++库第一步:动态库生成(vs2017+qt5.12.10)第二步:Java调用C++

springboot+dubbo实现时间轮算法

《springboot+dubbo实现时间轮算法》时间轮是一种高效利用线程资源进行批量化调度的算法,本文主要介绍了springboot+dubbo实现时间轮算法,文中通过示例代码介绍的非常详细,对大家... 目录前言一、参数说明二、具体实现1、HashedwheelTimer2、createWheel3、n

使用Python开发一个简单的本地图片服务器

《使用Python开发一个简单的本地图片服务器》本文介绍了如何结合wxPython构建的图形用户界面GUI和Python内建的Web服务器功能,在本地网络中搭建一个私人的,即开即用的网页相册,文中的示... 目录项目目标核心技术栈代码深度解析完整代码工作流程主要功能与优势潜在改进与思考运行结果总结你是否曾经

Java利用docx4j+Freemarker生成word文档

《Java利用docx4j+Freemarker生成word文档》这篇文章主要为大家详细介绍了Java如何利用docx4j+Freemarker生成word文档,文中的示例代码讲解详细,感兴趣的小伙伴... 目录技术方案maven依赖创建模板文件实现代码技术方案Java 1.8 + docx4j + Fr

SpringBoot首笔交易慢问题排查与优化方案

《SpringBoot首笔交易慢问题排查与优化方案》在我们的微服务项目中,遇到这样的问题:应用启动后,第一笔交易响应耗时高达4、5秒,而后续请求均能在毫秒级完成,这不仅触发监控告警,也极大影响了用户体... 目录问题背景排查步骤1. 日志分析2. 性能工具定位优化方案:提前预热各种资源1. Flowable