什么是代理模式
代理模式是结构型设计模式的一种,他的主要作用是为隔离(隐藏和保护)一个实现类。通过调用一个代理实现类的方法去调用被委托实现类的方法,达到一个隔离的目的。在代理类中可以添加一些功能(实施不同的控制功能),比如:负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。
为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。
更通俗的说,代理解决的问题:当两个类需要通信时,引入第三方代理类,将两个类的关系解耦,让我们只了解代理类即可,而且代理的出现还可以让我们完成与另一个类之间的关系的统一管理,但是切记,代理类和委托类要实现相同的接口,因为代理真正调用的还是委托类的方法。
按照代理的创建时期,代理类可以分为两种:
静态:由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。
动态:在程序运行时运用反射机制动态创建而成。
解决什么问题
下面是一些使用场景,不过太抽象,暂时可以不要在意,随着你的不断进步你终究会明白的。
远程代理 :为位于两个不同地址空间对象的访问提供了一种实现机制,可以将一些消耗资源较多的对象和操作移至性能更好的计算机上,提高系统的整体运行效率。
虚拟代理:通过一个消耗资源较少的对象来代表一个消耗资源较多的对象,可以在一定程度上节省系统的运行开销。
缓冲代理:为某一个操作的结果提供临时的缓存存储空间,以便在后续使用中能够共享这些结果,优化系统性能,缩短执行时间。
保护代理:可以控制对一个对象的访问权限,为不同用户提供不同级别的使用权限。
智能引用:要为一个对象的访问(引用)提供一些额外的操作时可以使用
静态代理模式
场景运用
以买卖房子为例,买卖房子除了需要买卖方资金谈好,还需要办理过户手续之类的。但是房东(委托类)不会搞这些乱七八糟,所以需用通过一个中介公司(代理类)来进行房子买卖。而买房(客户端)直接找房东(委托类)是不可能买到房子的,因为房东(委托类)缺少功能,所以买家(客户端)只能通过中介公司(委托类)来实现房子买卖功能。
代码演示
Isell接口:
public interface Isell {
void sell();
}
Mandator(房东委托类):
public class Mandator implements Isell{
public void sell(){
System.out.println("签合同,房子卖出。");
}
}
Proxy(中介代理类):
public class Proxy implements Isell{
private Isell man;
public Proxy(Isell man) {
this.man=man;
}
public void sell() {
System.out.println("办理过户手续");
man.sell();
}
}
Client客户类:
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
Isell buyer=new Proxy(new Mandator());
buyer.sell();
}
}
输出结果:
办理过户手续
签合同,房子卖出。
静态代理模式总结
优点:
代理使客户端不需要知道实现类是什么,怎么做的,只需要客户端知道代理就足够了,(解耦合)通过调用代理就可以完成实现。并且代理可以在实现类方法的前后添加其他方法的实现。
缺点:
1.代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
2.代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。如上的代码是只为一个类的访问提供了代理,但是如果还要为其他类提供代理的话,就需要我们再次添加代理其他类的代理类。
举例说明:代理可以对实现类进行统一的管理,如在调用具体实现类之前,需要打印日志等信息,这样我们只需要添加一个代理类,在代理类中添加打印日志的功能,然后调用实现类,这样就避免了修改具体实现类。满足我们所说的开闭原则。但是如果想让每个实现类都添加打印日志的功能的话,就需要添加多个代理类,以及代理类中各个方法都需要添加打印日志功能(如上的代理方法中删除,修改,以及查询都需要添加上打印日志的功能)
即静态代理类只能为特定的接口(Service)服务。如想要为多个接口服务则需要建立很多个代理类。
动态代理模式
根据如上的介绍,你会发现每个代理类只能为一个接口服务,这样程序开发中必然会产生许多的代理类
所以我们就会想办法可以通过一个代理类完成全部的代理功能,那么我们就需要用动态代理
在上面的示例中,一个代理只能代理一种类型,而且是在编译器就已经确定被代理的对象。而动态代理是在运行时,通过反射机制实现动态代理,并且能够代理各种类型的对象
在Java中要想实现动态代理机制,需要java.lang.reflect.InvocationHandler接口和 java.lang.reflect.Proxy 类的支持
java.lang.reflect.InvocationHandler接口的定义如下:
//Object proxy:被代理的对象
//Method method:要调用的方法
//Object[] args:方法调用时所需要参数
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
java.lang.reflect.Proxy类的定义如下:
//CLassLoader loader:类的加载器
//Class<?> interfaces:得到全部的接口
//InvocationHandler h:得到InvocationHandler接口的子类的实例
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
要调用这两个接口和类要import:import java.lang.refkect.*
代码实现
原代码的Isell接口和Mandator类不变,将代理类重写:
package java6;
import java.lang.reflect.*;
//动态代理类
public class myProxy implements InvocationHandler{
private Object targetobj;
//绑定关系,可以通过Proxy.newProxyInstance()来反射绑定所需的具体实现类,可获得其接口和方法信息,为invoke方法调用
public Object getProxy(Object targetobj) {
this.targetobj=targetobj;
return Proxy.newProxyInstance(targetobj.getClass().getClassLoader(),targetobj.getClass().getInterfaces(),this);
}
//关联的这个实现类的方法被调用时将被执行
/*InvocationHandler接口的方法,proxy表示代理,method表示原对象被调用的方法,args表示方法的参数*/
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable
{
//可添加对原方法的补充
System.out.println("办理过户手续");
//调用原方法
return method.invoke(targetobj,args);
}
}
动态类伪代码:
类 implements InvocationHandler
{
1.创建OBJ类
2.绑定关系。(创建一个方法得到代理对象)
{
调用Proxy.newProxyInStance方法,运用反射返回一个代理对象
return 代理对象;
}
3.实现接口方法invoke
{
添加自己需要的代码/方法;
//调用原方法
return method.invoke(Proxy,args);
}
}
因为这是动态代理,所以我们可以代理不同的类,不同接口,如新增卖车:
Car:
public interface Car {
void carsell();
}
Mandatorcar:
public class Mandatorcar implements Car{
public void carsell()
{
System.out.println("签合同,车子卖出。");
}
}
客户端:
package java6;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
myProxy proxy=new myProxy();
//代理卖方
Isell buyer=(Isell)proxy.getProxy(new Mandator());
buyer.sell();
//代理卖车
Car buy=(Car)proxy.getProxy(new Mandatorcar());
buy.carsell();
}
}
输出:
办理过户手续
签合同,房子卖出。
办理过户手续
签合同,车子卖出。
总结
可以看到输出动态代理为卖车和卖房都办理了过户手续。我们可以通过动态代理来代理不同的类,因为里面所有的方法都通过invoke函数,所以我们可以再invoke函数上添加一些方法/函数,比如:日志系统、事务、拦截器、权限控制等。这样使得志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码—解耦。
动态代理优点:
动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。而且动态代理的应用使我们的类职责更加单一,复用性更强
此文章大量参考/照搬该作:
https://blog.youkuaiyun.com/hejingyuan6/article/details/36203505?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param#commentBox
本文深入讲解Java中的代理模式,包括静态代理与动态代理的概念、应用场景及其实现方式。通过具体的代码示例,帮助读者理解如何利用代理模式进行解耦合编程。
1620

被折叠的 条评论
为什么被折叠?



