当前位置:首页 > CN2资讯 > 正文内容

java设计模式之代理模式(静态代理与JDK动态代理)

7小时前CN2资讯


定义:给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用;
目的:

(1)通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性;

(2)通过代理对象对原有的业务增强;

代理实现可以分为静态代理和动态代理。

静态代理

静态代理模式其实很常见,比如买火车票这件小事:黄牛相当于是火车站的代理,我们可以通过黄牛买票,但只能去火车站进行改签和退票。在代码实现中相当于为一个委托对象realSubject提供一个代理对象proxy,通过proxy可以调用realSubject的部分功能,并添加一些额外的业务处理,同时可以屏蔽realSubject中未开放的接口。

1、RealSubject 是委托类,Proxy 是代理类;
2、Subject 是委托类和代理类的接口;
3、request() 是委托类和代理类的共同方法;

具体代码实现如下:

interface Subject {
void request();
}

class RealSubject implements Subject {
public void request(){
System.out.println("RealSubject");
}
}

class Proxy implements Subject {
private Subject subject;

public Proxy(Subject subject){
this.subject = subject;
}
public void request(){
System.out.println("begin");
subject.request();
System.out.println("end");
}
}

public class ProxyTest {
public static void main(String args[]) {
RealSubject subject = new RealSubject();
Proxy p = new Proxy(subject);
p.request();
}
}

静态代理实现中,一个委托类对应一个代理类,代理类在编译期间就已经确定。

动态代理

动态代理中,代理类并不是在Java代码中实现,而是在运行时期生成,相比静态代理,动态代理可以很方便的对委托类的方法进行统一处理,如添加方法调用次数、添加日志功能等等,动态代理分为jdk动态代理和cglib动态代理,下面通过一个例子看看如何实现jdk动态代理。

接口方法:

public interface Service {
//目标方法
public abstract void add();
}

public class UserServiceImpl implements Service {
public void add() {
System.out.println("This is add service");
}
}

利用​​java.lang.reflect.Proxy​​​类和​​java.lang.reflect.InvocationHandler​​接口定义代理类的实现。

class MyInvocatioHandler implements InvocationHandler {
private Object target;

public MyInvocatioHandler(Object target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("-----before-----");
Object result = method.invoke(target, args);
System.out.println("-----end-----");
return result;
}
// 生成代理对象
public Object getProxy() {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class<?>[] interfaces = target.getClass().getInterfaces();
return Proxy.newProxyInstance(loader, interfaces, this);
}
}

使用动态代理

public class ProxyTest {
public static void main(String[] args) {
Service service = new UserServiceImpl();
MyInvocatioHandler handler = new MyInvocatioHandler(service);
Service serviceProxy = (Service)handler.getProxy();
serviceProxy.add();
}
}

执行结果:

-----before-----
This is add service
-----end-----

代理对象的生成过程由Proxy类的newProxyInstance方法实现,分为3个步骤:
1、​​​ProxyGenerator.generateProxyClass​​​方法负责生成代理类的字节码,生成逻辑比较复杂,有兴趣的同学可以继续分析源码 ​​sun.misc.ProxyGenerator​​;

// proxyName:格式如 "com.sun.proxy.$Proxy.1";
// interfaces:代理类需要实现的接口数组;
// accessFlags:代理类的访问标识;
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);

2、native方法​​Proxy.defineClass0​​负责字节码加载的实现,并返回对应的Class对象。

Class clazz = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);

3、利用​​clazz.newInstance​​反射机制生成代理类的对象;

反编译代理类
为了更清楚的理解动态代理,通过以下方式把代理类字节码生成class文件。

byte[] classFile = ProxyGenerator.generateProxyClass("com.sun.proxy.$Proxy.1", service.getClass().getInterfaces());
FileOutputStream out = new FileOutputStream("com.sun.proxy.$Proxy.1.class");
out.write(classFile);
out.flush();

使用 ​​反编译工具 jad​​​ ​​jad com.sun.proxy.$Proxy.1​​ 看看代理类如何实现,反编译出来的java代码如下:

public final class $proxy1 extends Proxy implements Service {

public $proxy1(InvocationHandler invocationhandler) {
super(invocationhandler);
}

public final boolean equals(Object obj) {
try {
return ((Boolean)super.h.invoke(this, m1, new Object[] {
obj
})).booleanValue();
}
catch(Error _ex) { }
catch(Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}

public final String toString() {
try {
return (String)super.h.invoke(this, m2, null);
}
catch(Error _ex) { }
catch(Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}

public final void add() {
try {
super.h.invoke(this, m3, null);
return;
}
catch(Error _ex) { }
catch(Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}

public final int hashCode() {
try {
return ((Integer)super.h.invoke(this, m0, null)).intValue();
}
catch(Error _ex) { }
catch(Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}

private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;

static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
Class.forName("java.lang.Object")
});
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("zzzzzz.Service").getMethod("add", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
}
catch(NoSuchMethodException nosuchmethodexception) {
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
}
catch(ClassNotFoundException classnotfoundexception) {
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
}

从上述代码可以发现:
1、生成的$proxy1继承自Proxy类,并实现了Service接口。
2、执行代理对象的方法,其实就是执行InvocationHandle对象的invoke方法,传入的参数分别是当前代理对象,当前执行的方法和参数。

super.h.invoke(this, m3, null);

jdk动态代理使用的局限性
通过反射类​​​Proxy​​​和​​InvocationHandler​​回调接口实现的jdk动态代理,要求委托类必须实现一个接口,但事实上并不是所有类都有接口,对于没有实现接口的类,便无法使用该方方式实现动态代理。

    你可能想看:

    扫描二维码推送至手机访问。

    版权声明:本文由皇冠云发布,如需转载请注明出处。

    本文链接:https://www.idchg.com/info/32067.html

    分享给朋友:

    “java设计模式之代理模式(静态代理与JDK动态代理)” 的相关文章

    美国服务器CN2线路图解:优化您的国际访问体验

    了解美国服务器CN2线路的基础与优势在数字化时代,网站的访问速度与稳定性是决定用户体验的关键因素,尤其是在跨国访问中,线路选择尤为重要。美国服务器作为全球互联网的重要节点之一,一直备受国内外用户的青睐。选择一款高性能的美国服务器不仅需要关注硬件配置,还需要深入了解其背后所依赖的国际线路,尤其是CN2...

    搬瓦工退款政策全解析:30天无理由退款,轻松解决用户疑虑

    搬瓦工退款政策的基本介绍 搬瓦工(BandwagonHost)作为国内知名的VPS服务提供商,以其性价比高、服务稳定而受到许多用户的青睐。在使用过程中,用户可能会因为各种原因需要申请退款。搬瓦工提供了30天无理由退款保证,确保用户在购买后的一段时间内享有退款的权利。这一政策不仅体现了搬瓦工对用户权益...

    如何有效解决VPS硬盘占用过高问题:优化与清理指南

    1.1 系统日志和缓存文件积累 系统日志和缓存文件是VPS硬盘占用过高的常见原因之一。每次系统运行或应用程序执行时,都会生成日志文件来记录操作和错误信息。这些日志文件随着时间的推移会逐渐积累,占用大量磁盘空间。缓存文件也是如此,它们用于加速系统或应用程序的运行,但如果不定期清理,也会占用大量空间。我...

    远程VPS优选指南:高效管理虚拟专用服务器的最佳实践

    随着远程工作的普及和数字化转型的加速,远程VPS(虚拟专用服务器)逐渐成为许多企业和个人的首选工具。VPS通过虚拟化技术,让我们能够在一台物理服务器上同时运行多个独立的操作系统,这种灵活性使得用户能够像管理独立服务器那样,远程登录和管理自己的虚拟环境。每天都有更多的人意识到,拥有一个VPS可以为他们...

    宝塔的设计概念与历史发展:信仰与文化的桥梁

    宝塔的设计概念 宝塔,这一宗教建筑的象征,体现了深厚的文化内涵和独特的美学设计。作为佛教的重要象征,宝塔的设计概念不仅包括其结构和形态的美感,也涵盖了宗教信仰的意义。最初的宝塔设计如同一个倒扣的钵盂,象征着对佛教教义的恭敬与追思。随着时间的推移,宝塔的构造和装饰逐渐演变,形成了今天我们所见的多样化风...

    AWS VPS Free: 如何利用AWS Free Tier免费服务轻松构建云计算项目

    当我第一次接触AWS (亚马逊网络服务) 的时候,最吸引我的就是他们提供的各种免费的VPS服务。AWS的VPS免费服务实际上是一种叫做AWS Free Tier的计划,它允许用户在一定条件下使用AWS的多种服务而无需支付费用。这项计划的意义在于,它为刚入门的开发者和小型企业提供了一个绝佳的机会,能够...