Java中代理的实现方式

news/2024/11/29 19:32:01/

Java中代理的实现方式

  • 1. 静态代理
  • 2. JDK动态代理
  • 3. CGLIB动态代理
    • 扩展

在这里插入图片描述

在Java中,有多种方式可以实现代理,包括:

  1. 静态代理:

    • 创建一个代理类,实现与目标类相同的接口或继承与目标类相同的父类。
    • 在代理类中持有一个目标类的引用。
    • 在代理类的方法中,调用目标类的对应方法,并在其前后执行额外的逻辑。
    • 使用代理对象来调用方法,实际上是通过代理类间接调用目标类的方法。
  2. JDK动态代理:

    • 定义一个接口,作为代理类和目标类的共同接口。
    • 创建一个实现InvocationHandler接口的代理类。
    • 通过Proxy.newProxyInstance()方法创建代理对象,传入目标类的类加载器、目标类实现的接口和代理类的实例。
    • 在代理类的invoke()方法中,可以在调用目标方法之前和之后执行额外的逻辑。
    • 使用代理对象来调用方法,实际上是通过代理类的invoke()方法间接调用目标类的方法。
  3. CGLIB动态代理:

    • 引入CGLIB库的依赖。
    • 创建一个实现MethodInterceptor接口的代理类。
    • 通过Enhancer类创建代理对象,设置目标类作为父类、代理类作为回调对象。
    • 在代理类的intercept()方法中,可以在调用目标方法之前和之后执行额外的逻辑。
    • 使用代理对象来调用方法,实际上是通过代理类的intercept()方法间接调用目标类的方法。

这些代理方式各有特点,可以根据具体需求选择适合的方式。静态代理相对简单,但需要为每个目标类编写一个代理类;JDK动态代理适用于基于接口的代理;CGLIB动态代理适用于没有实现接口的类的代理。

1. 静态代理

// 定义接口
interface Subject {void doSomething();
}// 目标类
class RealSubject implements Subject {@Overridepublic void doSomething() {System.out.println("RealSubject is doing something.");}
}// 代理类
class ProxySubject implements Subject {private RealSubject realSubject;public ProxySubject(RealSubject realSubject) {this.realSubject = realSubject;}@Overridepublic void doSomething() {System.out.println("ProxySubject is doing something before RealSubject.");realSubject.doSomething();System.out.println("ProxySubject is doing something after RealSubject.");}
}// 使用示例
public class StaticProxyExample {public static void main(String[] args) {RealSubject realSubject = new RealSubject();ProxySubject proxySubject = new ProxySubject(realSubject);proxySubject.doSomething();}
}

在上面的示例中,Subject是一个接口,RealSubject是目标类,实现了Subject接口。ProxySubject是代理类,也实现了Subject接口。在ProxySubject中,我们持有一个RealSubject的引用,并在调用doSomething()方法之前和之后执行额外的逻辑。

在StaticProxyExample中,我们创建了一个RealSubject对象和一个ProxySubject对象,并调用proxySubject.doSomething()方法。在执行过程中,ProxySubject会在调用RealSubject的doSomething()方法之前和之后输出额外的信息,从而实现对目标类的增强。

这就是一个简单的静态代理示例,通过代理类包装目标类,实现了对目标类的功能增强。

2. JDK动态代理

首先,定义一个接口和一个实现该接口的目标类:

// 定义接口
interface Subject {void doSomething();
}// 实现接口的目标类
class RealSubject implements Subject {@Overridepublic void doSomething() {System.out.println("RealSubject is doing something.");}
}

然后,创建一个实现InvocationHandler接口的代理类:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;// 代理类
class ProxyHandler implements InvocationHandler {private Object target;public ProxyHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Proxy is doing something before RealSubject.");Object result = method.invoke(target, args);System.out.println("Proxy is doing something after RealSubject.");return result;}
}

在代理类的invoke()方法中,我们可以在调用目标方法之前和之后执行额外的逻辑。
最后,创建代理对象并调用方法:

import java.lang.reflect.Proxy;public class JDKDynamicProxyExample {public static void main(String[] args) {RealSubject realSubject = new RealSubject();ProxyHandler proxyHandler = new ProxyHandler(realSubject);// 创建代理对象Subject proxySubject = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(),realSubject.getClass().getInterfaces(),proxyHandler);// 调用代理对象的方法proxySubject.doSomething();}
}

在上面的示例中,我们创建了一个RealSubject对象和一个ProxyHandler对象。然后,使用Proxy.newProxyInstance()方法创建代理对象,传入目标类的类加载器、目标类实现的接口和代理类的实例。通过代理对象调用方法时,实际上是调用了代理类的invoke()方法,在其中执行了额外的逻辑,并最终调用目标对象的方法。

这就是一个简单的使用JDK动态代理的示例,通过代理类和InvocationHandler接口,我们可以在运行时动态地生成代理对象,并在调用目标方法之前和之后执行额外的逻辑。

3. CGLIB动态代理

首先,确保在项目中添加CGLIB库的依赖。

然后,定义一个目标类:

// 目标类
class RealSubject {public void doSomething() {System.out.println("RealSubject is doing something.");}
}

接下来,创建一个实现MethodInterceptor接口的代理类:

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;// 代理类
class ProxyInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("Proxy is doing something before RealSubject.");Object result = proxy.invokeSuper(obj, args);System.out.println("Proxy is doing something after RealSubject.");return result;}
}

在代理类的intercept()方法中,我们可以在调用目标方法之前和之后执行额外的逻辑。在CGLIB动态代理中,通过MethodProxy对象来调用目标方法。

最后,创建代理对象并调用方法:

import net.sf.cglib.proxy.Enhancer;public class CGLIBDynamicProxyExample {public static void main(String[] args) {ProxyInterceptor proxyInterceptor = new ProxyInterceptor();// 创建Enhancer对象Enhancer enhancer = new Enhancer();enhancer.setSuperclass(RealSubject.class);enhancer.setCallback(proxyInterceptor);// 创建代理对象RealSubject proxySubject = (RealSubject) enhancer.create();// 调用代理对象的方法proxySubject.doSomething();}
}

在上面的示例中,我们创建了一个ProxyInterceptor对象,并使用Enhancer类来创建代理对象。通过setSuperclass()方法指定目标类,通过setCallback()方法指定代理类。最后,调用create()方法创建代理对象。

通过代理对象调用方法时,实际上是调用了代理类的intercept()方法,在其中执行了额外的逻辑,并最终调用目标对象的方法。

这就是一个简单的使用CGLIB动态代理的示例,通过代理类和MethodInterceptor接口,我们可以在运行时动态地生成代理对象,并在调用目标方法之前和之后执行额外的逻辑。

扩展

CGLIB在实现动态代理时使用了ASM库。ASM是一个Java字节码操作和分析框架,可以用于在运行时生成或修改字节码。CGLIB利用ASM库来生成代理类的字节码,以实现对目标类的动态代理。

在CGLIB中,Enhancer类使用了ASM库来生成代理类的字节码。ASM提供了一套API,可以直接操作字节码,包括创建类、添加字段、添加方法、修改方法等。CGLIB利用ASM的API来生成代理类的字节码,并将其加载到JVM中。

通过使用ASM库,CGLIB能够在运行时生成代理类的字节码,而无需依赖目标类的接口。这使得CGLIB可以代理那些没有实现接口的类,提供了更大的灵活性。


http://www.ppmy.cn/news/1112904.html

相关文章

uniapp——实现在线选座功能——技能提升

首先声明一点:下面的内容是从一个uniapp的程序中摘录的,并非本人所写,先做记录,以免后续遇到相似需求抓耳挠腮。 这里写目录标题 效果图代码——html部分cu-custom组件anil-seat组件 代码——jscss部分 效果图 代码——html部分 …

IDEA最新激 20活23码

人狠话不多 大家好,最近Intelli Idea官方的校验规则进行了更新,之前已经成功激20活23的Idea可能突然无法使用了。 特地从网上整理了最新、最稳定的激20活23码分享给大家,希望可以帮助那些苦苦为寻找Idea激20活23码而劳累的朋友们。 本激23…

SpaceX预计到2022年Starlink用户将达到2000万,但最终达到了100万

SpaceX的Starlink部门还没有接近实现客户和收入的预测,该公司在建立卫星网络之前与投资者分享了这一点华尔街日报报道今天出版。 据报道,2015年的一份题为“SpaceX用来从投资者那里筹集资金”的报告预计,到2022年,Starlink的订户…

git git fetch 和 git fetch origin master 的区别

git fetch 第1步 先读取 .git/config 配置 [remote origin],若 fetch 并没有指定其中一个或多个远程仓库,就会处理所有的远程仓库 [remote “origin”]url gitgithub.com:kaku/testGit.gitfetch refs/heads/:refs/remotes/origin/第2步 git fetch 会…

《动手学深度学习 Pytorch版》 5.5 读写文件

5.5.1 加载和保存 import torch from torch import nn from torch.nn import functional as Fx torch.arange(4) torch.save(x, x-file) # 使用 save 保存x2 torch.load(x-file) # 使用 load 读回内存 x2tensor([0, 1, 2, 3])y torch.zeros(4) torch.save([x, y],x-files…

JavaScript展开操作符(Spread operator)的应用详解

你可以通过展开操作符(Spread operator)...扩展一个数组对象和字符串。展开运算符(spread operator)是三个点(…),可以将可迭代对象转为用逗号分隔的参数序列,也就是说将可迭代的对象的值传递到函数的参数中。如同rest…

Leetcode168. Excel表列名称

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 题解: 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 代码如下: class Solution {public String convertToTitle(int columnNumber) {StringBuild…

观察者模式 发布-订阅模式(设计模式与开发实践 P8)

文章目录 观察者模式运用实现 观察者模式 定义:他用来定义对象之间一种一对多的依赖关系,当一个对象状态发生改变时,所有依赖他的对象都会得到通知 运用 如果我们使用过 DOM 上的事件函数,那就接触过观察者模式 document.body…