标签:not 远程 目标 方法 现在 访问权限 lse 本地 override
当直接访问某些对象存在问题时,可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,所访问的真实对象与代理对象需要实现相同的接口。
某些情况下,一个客户不想或不能直接引用一个对象,此时可以通过一个称之为代理的第三者实现间接引用。代理对象在客户端和目标对象之间起到中介作用,并且可以通过代理对象去掉客户不能看到的内容和添加客户需要的额外服务。
给某一个对象提供一个代理,并由代理对象控制对原对象的引用。代理模式的英文叫做 Proxy 或 Surrogate,它是一种对象结构模式。
Subject(抽象主题角色)
声明了真实主题和代理主题的公共接口,这样一来在任何使用真实主题的地方都可以使用代理主题。客户端针对抽象主题角色编程。
Proxy(代理主题角色)
代理主题角色内部包含对真实主题的引用,从而可以在任何时候操作真实主题角色。
代理主题角色中提供一个与真实主题角色相同的接口,以便在任何时候替代真实主体。
代理主题角色还可以控制对真实主题的使用,负责在需要时创建和删除真实主题对象,并对真实主题对象的使用加以约束。
代理角色通常在客户端调用所引用的真实主题操作之前或之后执行其他操作,而不仅仅只是单纯调用真实主题对象中的操作。
RealSubject(真实主题角色)
真实主题角色定义了代理角色所代表的真实对象,真实主题角色中实现真实的业务,客户端通过代理主题角色间接调用真实主题角色中定义的方法。
在一个论坛已注册用户和游客权限不同,已注册用户拥有发帖、修改注册信息、修改自己帖子等功能;而游客只能看到别人发的贴子,没有其他权限。本实例中我们使用代理模式中的保护代理,该代理用于控制对一个对象的访问,可以给不同用户提供不同级别的使用权限。
抽象主题角色 AbstractPermission(抽象权限类)
AbstractPermission 作为抽象权限类,充当抽象主题角色,在其中声明了真实主题角色所提供的业务方法,它是真实主题角色和代理主题角色的公共接口
public interface AbstractPermission {
public void modifyUserInfo();
public void viewNote();
public void publishNote();
public void modifyNote();
public void setLevel(int level);
}
真实主题角色 RealPermission(真实权限类)
RealPermission 是真实主题角色,它实现了在抽象主题角色中定义的方法,由于种种原因客户端无法访问其中内容。
public class RealPermission implements AbstractPermission {
@Override
public void modifyUserInfo() {
System.out.println("修改用户信息");
}
@Override
public void viewNote() { }
@Override
public void publishNote() {
System.out.println("发布新帖");
}
@Override
public void modifyNote() {
System.out.println("修改发帖内容");
}
@Override
public void setLevel(int level) { }
}
代理主题角色 PermissionProxy(权限代理类)
PermissionProxy 是代理主题角色,它也实现了抽象主题角色接口,同时在 PermissionProxy 中定义了一个 RealPermission 对象,用于调用 RealPermission 中定义的真实业务方法。通过引入 PermissionProxy 类来对系统的使用权限进行控制,这就是保护代理的用途。
public class PermissionProxy implements AbstractPermission {
private RealPermission permission = new RealPermission();
private int level = 0;
@Override
public void modifyUserInfo() {
if (0 == level) {
System.out.println("对不起,你没有该权限");
} else if (1 == level) {
permission.modifyUserInfo();
}
}
@Override
public void viewNote() {
System.out.println("查看帖子");
}
@Override
public void publishNote() {
if (0 == level) {
System.out.println("对不起,你没有该权限");
} else if (1 == level) {
permission.publishNote();
}
}
@Override
public void modifyNote() {
if (0 == level) {
System.out.println("对不起,你没有该权限");
} else if (1 == level) {
permission.modifyNote();
}
}
@Override
public void setLevel(int level) {
this.level = level;
}
}
客户端测试类 Client
public class Client {
public static void main(String[] args) {
AbstractPermission permission = new PermissionProxy();
permission.modifyUserInfo();
permission.viewNote();
permission.publishNote();
permission.modifyNote();
System.out.println("-------------------------");
permission.setLevel(1);
permission.modifyUserInfo();
permission.viewNote();
permission.publishNote();
permission.modifyNote();
}
}
运行结果
代理类可以对用户访问权限进行控制,因此有些用户无权调用真实业务类的某些方法,当用户权限改变时,则可以访问这些方法。如果需要增加并使用新的代理类,首先将新增代理类作为抽象主题角色的子类,实现在抽象主题中声明的方法。
代理模式优点如下:
代理模式缺点如下:
根据代理模式的使用目的,常见的代理模式有以下几个类型。
所谓静态代理,就是由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的 .class 文件就已经生成。简单来说,上述的实例就属于静态代理,PermissionProxy 代理类是我们定义好的,在程序运行之前就已经编译完成。
传统的代理模式中,客户端通过 ProxySubject 调用 RealSubject 类的方法,同时还在代理类中封装了其他方法,可以处理一些其他问题。如果按照这种方法使用代理模式,那么真实主题角色必须是是事先已经存在的,并将其作为代理对象的内部成员属性。如果一个真实主题角色必须对应一个代理主题角色,这将导致系统中的类的个数急剧增加,因此需要想办法减少系统中类的个数。
Java 自带的基于接口的动态代理(即只能实现接口的代理)能在运行时根据我们在 Java 代码中的指示动态生成的代理类,其实现相关类位于 java.lang.reflect 包,运行时动态地对某些东西作代理,主要涉及两个类
InvocationHandler 接口
InvocationHandler is the interface implemented by the invocation handler of a proxy instance.
Each proxy instance has an associated invocation handler.When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.
InvocationHandler 是由代理实例的调用处理程序实现的接口。每个代理实例都有一个关联的调用处理程序。InvocationHandler 接口中定义了 invoke 方法,当代理实例调用某个方法时,该方法的调用将被编码并调度到其调用处理程序的 invoke 方法处理。
/**
* 处理代理实例上的方法调用并返回结果
* proxy 表示动态代理类
* method 表示需要代理的方法
* args 表示代理方法的参数数组
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
Proxy 类
/**
* 该类提供了用于为接口创建代理实例的静态方法
*/
public class Proxy implements java.io.Serializable {
...
/**
* 根据传入的接口类型返回一个动态创建的代理类实例
* loader 表示被代理类的类加载器
* interfaces 表示被代理类实现的接口列表(与真实主题类的接口列表一致)
* h 表示所指派的调用处理程序类
*/
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {
...
}
下面通过一个简单实例来学习动态代理,现在有两个真实主题类分别是 RealSubjectA 和 RealSubjectB,它们对于抽象主题类中定义的抽象方法 request() 提供了不同的实现,在不增加新的代理类的情况下,使得客户端通过一个代理类来动态选择所代理的真实主题对象
抽象主题接口 AbstractSubject
public interface AbstractSubject {
public void request();
}
真实主题类一 RealSubjectA
public class RealSubjectA implements AbstractSubject {
@Override
public void request() {
System.out.println("真实主题类A");
}
}
真实主题类二 RealSubjectB
public class RealSubjectB implements AbstractSubject {
@Override
public void request() {
System.out.println("真实主题类B");
}
}
动态代理类 DynamicProxy
public class DynamicProxy implements InvocationHandler {
private Object obj;
public DynamicProxy() {}
public DynamicProxy(Object obj) {
this.obj = obj;
}
// 实现 invoke() 方法,调用在真实主题类中定义的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("调用之前");
// 利用反射调用方法,如果方法没有返回值则为 null
Object result = method.invoke(obj, args);
System.out.println("调用之后");
return result;
}
}
客户端测试类 Client
public class Client {
public static void main(String[] args) {
AbstractSubject subject = new RealSubjectA();
InvocationHandler handler = new DynamicProxy(subject);
AbstractSubject subjectProxy = (AbstractSubject) Proxy.newProxyInstance(subject.getClass().getClassLoader(),
subject.getClass().getInterfaces(), handler);
subjectProxy.request();
System.out.println("-----------------------------");
subject = new RealSubjectB();
handler = new DynamicProxy(subject);
subjectProxy = (AbstractSubject) Proxy.newProxyInstance(subject.getClass().getClassLoader(),
subject.getClass().getInterfaces(), handler);
subjectProxy.request();
}
}
运行结果
标签:not 远程 目标 方法 现在 访问权限 lse 本地 override
原文地址:https://www.cnblogs.com/Yee-Q/p/12891869.html