当前位置: 首页 > news >正文

从回调认识动态代理 (Java)

介绍

之前的动态代理篇幅写的不好,所以重写了一下(Java实现),作为个人的复习3。同时这一章承上启下,承接反射篇章,引出AOP思想和Spring AOP

了解AOP的前置知识(本人目前只了解Java AOP)

  • 回调函数
  • 静态代理
  • 反射
  • jdk动态代理

回调方法 CallBack

将核心代码交给使用者编写

A想使用B设计的代码,其中的大部分功能符合A需求,但是A仍不满足,如果代码是写死的话,A还要重构一下并自己封装,冗余而不方便。

假设B的代码将A想重写的部分抽离出去,其余部分做一个框架,A就只需要写核心的代码即可,非常方便。所以被抽离的部分就是回调函数!

想要实现这个思想,各个语言各有不同的方法(noob的猜测);Java要实现回调函数,就要利用其重要的面向接口(Interface)思想

1.回调接口

public interface CallBack {public void callBackMethod();
}

2.调用类

public class Caller {public void call(CallBack callBack) {System.out.println("无关代码");callBack.callBackMethod();System.out.println("");}
}

3.重写回调方法,为了方便使用匿名内部类

public static void main(){Caller caller = new Caller();caller.call(new CallBack(){@Overridepublic void callBackMethod(){System.out.println("");}});
}
/*
无关代码
回调方法: Hello I'm Oddpalmer
无关代码
*/

只需要编写一个继承callback的类,并重写方法即可传入Caller实现回调,it's simple

静态代理 Static Proxy

代理就是将非核心代码剥离出去,只关注对象本身的核心
非核心代码在AOP中称为通知(Advice)

以明星和经纪人为例:对接、签约....,这些工作经纪人做就行,明星只需要唱歌、演戏....即可,这里经纪人就作了代理的工作,并且经纪人可以去为多个明星服务。

Java实现代理的步骤

  • 代理和被代理对象类继承同一个接口
  • 代理的方法调用对象的同名方法

依旧是面向接口编程

1.接口

public interface UserService {void addUser(String name);
}

2.实现类

public class UserServiceImpl implements UserService{@Overridepublic void addUser(String name) {System.out.println("添加用户 " + name + " 成功");}
}

3.实现类代理

public class UserServiceProxy implements UserService{private UserService target;public UserServiceProxy(UserService target) {this.target = target;}@Overridepublic void addUser(String name) {System.out.println("代理:[权限检查]");// 前置额外逻辑target.addUser(name); //UserService真实对象的核心业务逻辑System.out.println("代理:[日志上传]");// 后置额外逻辑}
}

4.Main

// 调用
public class ProxyTest {public static void main(String[] args) {UserService proxy = new UserServiceProxy(new UserServiceImpl());proxy.addUser("张三");}
}
/*  结果代理:[权限检查]添加用户 张三 成功代理:[日志上传]
*/

虽然静态代理能在一定程度上帮我们减少代码冗余,但是不难发现只有继承了UserService接口的类才可以被代理。如果我还有OrderService、ProductService也需要权限检查或者日志上传,就需要多个静态代理才可以实现,局促而不优雅,这就引出了我们的动态代理。

动态代理 Dynamic Proxy

我们已经知道静态代理与被代理对象的类会继承同一个接口。那么Java实现动态代理也要知道创造的代理类要继承什么接口,从而和被代理对象在接口上保持一致!所以就要依赖Java反射机制,以获取运行时类的接口信息和方法,然后由JVM在内存中动态创造多个静态代理类,实现代理!(也称作JDK动态代理,用接口实现。cglib本文不作讨论)

不了解反射的可以看博主的这篇文档 待重构todo

jvm生成的一个动态代理类大致长这样,发现没有,和静态代理一样的,区别只是动态生成的(里面涉及了一个回调,我们稍后再讲),DeepSeek给出的代码:

public final class $Proxy0 extends Proxy implements UserService {private static Method m_addUser;static {try {m_addUser = UserService.class.getMethod("addUser", String.class);} catch (NoSuchMethodException e) {throw new RuntimeException(e);}}// 父类 Proxy 已经持有 InvocationHandler hpublic $Proxy0(InvocationHandler h) {super(h);}@Overridepublic void addUser(String name) {try {// invoactionHandler.invoke(), 所有接口方法 → 回调调用者h.invoke(this, m_addUser, new Object[]{name});} catch (RuntimeException | Error e) {throw e;} catch (Throwable t) {throw new UndeclaredThrowableException(t);}}
}

如何生成一个动态代理类?Java给我们设计了java.lang.reflect.Proxy类,其中的newProxyInstance()的三个参数 就是关键...

public static Object newProxyInstance(ClassLoader loader,     // 用target类加载器Class<?>[] interfaces,  // 用target的接口:硬性规定只能代理接口InvocationHandler h    // 传入回调实现类: 要求重写回调方法,用于承载“方法调用时的统一处理逻辑”。
)

参数1涉及的底层我还理解不了;参数2很明显是通过反射获取某个类的接口;参数3就是我们上文提到的回调接口,参数如下:

Interface InvotationHandler{public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

创造代理对象的步骤:

  • 调用newProxyInstance()
  • 将被代理对象的实例作为参数传入
  • 重写invocationHandler回调接口的invoke()

实际开发中,动态代理可以再封装成一个工具类,不用每次代理都去newProxyInstance(),简化代码

  public class ProxyUtil {@SuppressWarnings("unchecked")public static <T> T createProxy(T target) {return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() {@Override// InvokeHandler接口定义的回调方法,控制权在程序员手里public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("[权限检测]");// 前置 额外逻辑Object o = method.invoke(target, args);// 反射包下invoke 调用真实对象方法System.out.println("[日志记录]");// 后置 额外逻辑return o;}});}
}

当你调用了ProxyUtil后

	UserService userServiceProxy = new ProxyUtil().createProxy(new UserServiceImple());userServiceProxy.addUser("oddpalmer");/*[权限检测]添加 oddpalmer 成功[日志记录]*/

程序执行顺序:

  • jvm生成了动态代理类$ProxyXXX
  • main调用代理类UserServiceProxy的addUser(),会通过重写的回调方法调用被代理对象本身的addUser()

引出Spring AOP

面向切面AOP的思想很简单,将一个对象的核心代码抽离出来,使得开发者更注重核心代码而非其他的冗余代码(通知)。动态代理就是实现这一思想的利器

Spring AOP就很好的帮我们省去了写ProxyUtil类的过程....

http://www.jsqmd.com/news/701058/

相关文章:

  • 自学网络安全的三个必经阶段(含路线图)_网络安全自学路线
  • 内存安全不是选配项:工信部《智能网联汽车软件供应链安全指引(2026试行版)》第3.2.1条强制要求C项目启用-Mmemory-safety=strict,否则不予准入
  • BepInEx游戏插件框架:3分钟解锁你的游戏无限可能 [特殊字符]
  • 你的岗位没了,但有人比你更忙
  • 优先级函数:实时系统开发的革命性范式
  • 晶圆制造行业展会哪家好?精选制造领域展会推动产业技术创新升级 - 品牌2026
  • 2026年Q2技术分享:负载车出租、静音发电机出租、高压容性负载租赁、ups不间断电源出租、中压发电车、假负载测试租赁选择指南 - 优质品牌商家
  • 【2026年美团暑期实习- 4月25日-算法岗-第三题- 小美的异或问题】(题目+思路+JavaC++Python解析+在线测试)
  • Mermaid在线图表编辑器终极指南:5分钟从零到专业图表制作
  • 量子启发KAN-LSTM:时序建模新架构解析
  • 量子LDPC码波束搜索解码器:高效纠错技术解析
  • 2026大功率太阳能路灯厂家排行:成都市政太阳能路灯、成都庭院灯定制、成都庭院灯工程批发、成都户外太阳能路灯、成都户外庭院灯选择指南 - 优质品牌商家
  • 【测试日常】记录一次兼容性Bug的排查处理过程
  • 集成学习算法:原理、实现与优化指南
  • 从零到精通:AI大模型学习路线全解析!AI大模型学习路线(非常详细)收藏这一篇就够了
  • Gitee CodePecker SCA:构建企业级软件供应链安全新防线
  • 量子误差缓解NIL框架:原理、实现与应用
  • 如何实现百度网盘直链解析:专业开发者的高速下载解决方案
  • Linux 的 split 命令
  • 【2026年美团暑期实习- 4月25日-算法岗-第四题- 树上操作】(题目+思路+JavaC++Python解析+在线测试)
  • 为什么你的FP16算子在CUDA 13.2上反而变慢?深度解析Warp Matrix Instructions兼容性陷阱(附NVCC编译参数黄金组合)
  • AI智能体核心原理:从OpenAI函数调用到自主任务循环的百行代码实现
  • 生态共赢:Ledger与秘语盾达成战略合作,共建可信安全网络
  • 量子化学计算与变分量子算法在分子模拟中的应用
  • RainbowGPT本地化部署实战:中文优化大模型从入门到生产级应用
  • VTJ.PRO v2.3.8 版本发布:接入 DeepSeek V4,多项功能升级提升开发者体验
  • 深度学习核心技术解析:从神经网络到AI应用
  • 数字孪生遇上AI:电磁仿真的“智能革命”全解析
  • Keras实现Mask R-CNN目标检测与实例分割实战
  • NumPy张量操作与机器学习应用指南