从源码解析代理模式

news/2024/11/16 7:30:31/

大纲

代理模式(结构型设计模式)通过代理类去访问实现类中的方法,使用场景比如:已有接口和实现类的情况下,想要在已实现的方法基础上扩展更多的功能的场景。

代理模式里的主要类:

  1. 接口

  1. 实现类,需实现接口,用来完成真正的业务。(网上资料也叫做委托类、被代理类)

  1. 代理类,也需要实现接口,调用实现类的方法,本身不处理业务。

使用代理模式的好处:可以扩展实现类的功能,在实现类基本功能基础上增加一些额外的操作。

静态代理模式

图示:

要想做到在原来实现方法的基础上多增加功能,代理类同样也需要实现接口,并且需要持有一个实现类,在自己实现的方法中扩展功能,并且调用实现类的方法去处理真正的业务:

public class TV {String name;String model;public String getModel() {return model;}public String getName() {return name;}public void setModel(String model) {this.model = model;}public void setName(String name) {this.name = name;}public TV(String name,String model){this.name = name;this.model = model;}@Overridepublic String toString() {return "TV{" +"name='" + name + '\'' +", model='" + model + '\'' +'}';}
}

实体类TV.java

public interface ICompany {TV produceTV();
}

接口ICompany.java

实现类需要实现ICompany接口:

/**
* 实现类
*/
public class CompanyFactory implements ICompany{@Overridepublic TV produceTV() {System.out.println("生产商生产一台电视");return new TV("TCL","40寸");}
}

代理类也需要实现ICompany接口:

/**
* 代理类
* 代理类和实现类都要实现接口
*/
public class TVProxy implements ICompany {ICompany company;@Overridepublic TV produceTV() {System.out.println("代理收到一笔订单");if (Objects.isNull(company)) {company = new CompanyFactory();}return company.produceTV();}
}

CompanyFactory.java

运行时构建代理类,调用代理类的方法同时也调用了实体类的实现方法:

public class MainClass {public static void main(String[] args) {ICompany company = new TVProxy();TV tv = company.produceTV();System.out.println(tv.toString());}
}

总结:

静态代理通过创建代理类去实现接口,在实现方法里添加额外功能,从而实现不改变原有实现方法下做到功能扩展。

动态代理模式

静态代理有什么缺点?(先别看下面内容思考一下)

静态代理的局限在于,如果接口新增了方法,则代理类必须实现该方法,而使用动态JDK代理,代理类不用实现接口。

实体类TV不变,接口增加维修方法,如果是静态代理,接口新增方法,则对应的实现类和代理类都要实现这个方法,而动态代理则在代码编写层面不用实现接口的所有方法:

图示:

接口新增repairTV方法:

public interface ICompany {TV produceTV();void repairTV(String name); //新增维修电视的方法
}

实现类需要实现这个方法:

public class CompanyFactory implements ICompany {@Overridepublic TV produceTV() {System.out.println("生产商生产一台电视");return new TV("TCL", "40寸");}@Overridepublic void repairTV(String name) {System.out.println(String.format("生产商维修%s电视",name));}
}

这里是动态代理类,可以看到,这里并没有实现ICompany接口了

public class TVDynamicProxy {Object factory;public TVDynamicProxy(Object factory) {this.factory = factory;}public <T> T getProxy(Class<T> clazz) {//newProxyInstance实际上是返回了一个实现了接口的代理类实例,这个实例是存在在内存中的,动态生成的:return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{clazz},new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //回调System.out.println("动态代理执行:接收到一笔订单");Object tv = method.invoke(factory, args); //这里通过反射调用了factory里实现的方法System.out.println("动态代理结束");return tv;}});}}

TVDynamicProxy.java

上面方法中newProxyInstance实际上是返回了一个实现了接口的代理类实例,这个实例是存在在内存中的,动态生成的

public class MainClassB {public static void main(String[] args) {ICompany company = new CompanyFactory();//将生成的代理类写到本地:System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true"); //将内存中的$Proxy0.class写到本地ICompany company1 = new TVDynamicProxy(company).getProxy(ICompany.class);TV tv = company1.produceTV();company1.repairTV(tv.name);}
}

代码运行查看结果

运行后断点可以看到,这里实际上返回了$Proxy0

$Proxy0实现了接口,并实现了接口里定义的方法。

所以先前我们说,是在代码编写层面上不用去实现接口,实际是JDK帮我们自动实现了。

public final class $Proxy0 extends Proxy implements ICompany {private static Method m1;private static Method m3;private static Method m2;private static Method m4;private static Method m0;public $Proxy0(InvocationHandler var1) throws  {super(var1);}public final boolean equals(Object var1) throws  {try {return (Boolean)super.h.invoke(this, m1, new Object[]{var1});} catch (RuntimeException | Error var3) {throw var3;} catch (Throwable var4) {throw new UndeclaredThrowableException(var4);}}//自动实现了接口里定义的方法public final TV produceTV() throws  {try {return (TV)super.h.invoke(this, m3, (Object[])null); //这里回调了InvocationHandler的invoke方法} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}public final String toString() throws  {try {return (String)super.h.invoke(this, m2, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}public final void repairTV(String var1) throws  {try {super.h.invoke(this, m4, new Object[]{var1});} catch (RuntimeException | Error var3) {throw var3;} catch (Throwable var4) {throw new UndeclaredThrowableException(var4);}}public final int hashCode() throws  {try {return (Integer)super.h.invoke(this, m0, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}...
}

$Proxy0.class

这个$Proxy0生成到了我们的根目录下面

总结:

动态代理在运行后会在内存中创建一个实现了接口的代理类,通过调用代理类的实现方法,回调到InvocationHandler接口的invoke方法,通过反射拿到实现类的方法,并进行调用。

思考:

如果我新建一个工厂类,不实现ICompany接口,那是否可以代理成功?

public class FactoryB {public TV produceTVB() {System.out.println("B工厂生产电视");return new TV("华为电视机", "南京");}
}

上面问题可以使用Cglib代理解决。有兴趣的同学可以看给出的参考链接自行拓展。

参考资料:

https://segmentfault.com/a/1190000040407024


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

相关文章

c++函数(2)

这里写自定义目录标题默认参数函数重载递归函数变量周期默认参数 可为形参指定默认值&#xff0c;如果在函数调用时&#xff0c;没有指定与形参对应的实参时&#xff0c;就自动使用默认值。 默认参数可简化复杂函数的调用。 默认参数在函数名第一次出现在程序中指定&#xff0…

kubernetes资源对象应用类(二)

kubernetes资源对象应用类&#xff08;二&#xff09; Service 的 ClusterIP 地址 既然每个 Pod 都会被分配一个单独的 IP 地址&#xff0c;而且每个 Pod 都提供了一个独立的 Endpoint&#xff08;Pod IP containerPort&#xff09;以被客户端访问&#xff0c;那么现在多个 P…

蓝桥杯刷题014——求阶乘(二分法)

求阶乘 蓝桥杯2022省赛题目 问题描述 满足 N ! 的末尾恰好有 K 个 0 的最小的 N 是多少? 如果这样的 N 不存在输出 −1 。 输入格式 一个整数 K 。 输出格式 一个整数代表答案。 样例输入 2样例输出 10评测用例规模与约定 对于 30% 的数据, 1≤K≤10^6. 对于 100% 的数据, 1…

水面漂浮物垃圾识别检测系统 YOlOv7

水面漂浮物垃圾识别检测系统通过PythonYOLOv7网络模型&#xff0c;实现对水面漂浮物以及生活各种垃圾等全天候24小时不间断智能化检测。Python是一种由Guido van Rossum开发的通用编程语言&#xff0c;它很快就变得非常流行&#xff0c;主要是因为它的简单性和代码可读性。它使…

【每日一道智力题】之坤坤猜生日(面试高频)

&#x1f680;write in front&#x1f680; &#x1f4dc;所属专栏&#xff1a;每日一题 &#x1f6f0;️博客主页&#xff1a;睿睿的博客主页 &#x1f6f0;️代码仓库&#xff1a;&#x1f389;VS2022_C语言仓库 &#x1f3a1;您的点赞、关注、收藏、评论&#xff0c;是对我最…

ArrayList类

ArrayList类 目录ArrayList类一、构造方法摘要1.1 ArrayList()1.2 ArrayList(Collection c)1.3 ArrayList(int initialCapacity)二、 ArrayList的扩容机制&#xff1a;2.1 源码如下&#xff1a;2.2. 以上扩容机制的弊端&#xff1a;三、代码案例ArrayList类一、构造方法摘要 1…

【C语言课堂】 函数递归

欢迎来到 Claffic 的博客 &#x1f49e;&#x1f49e;&#x1f49e; 前言&#xff1a; 时隔多日&#xff0c;来还欠大家的 C 语言学习啦&#xff0c;上期讲了函数&#xff0c;其实函数中应该包括函数递归的&#xff0c;这里单独拿出来讲解的原因是函数递归属于重难知识&#xf…

8. 好客租房-WebSocket与即时通讯系统[项目必需]

本章节主要是学习WebSocket, 目标快速入门, 能够回答以下问题:WebSocket和HTTP的区别.WebSocket使用的是全双工通讯协议, 与其他类似协议有啥区别?WebSocket中的常用注解有哪些&#xff1f;通过本章节的学习, 应该可以回答上来这几个问题.8.1 WebSocket概念快速理解WebSocket …