《大话设计模式》读书笔记:代理模式与Java的Proxy动态代理

本文深入介绍了Java中的代理模式,探讨了其应用场景,包括远程代理、虚拟代理、安全代理及智能指引。文章还详细讲解了如何利用Java动态代理实现对对象访问的控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

代理模式(Proxy),为其他对象提供一种代理以控制对这个对象的访问。

在面向对象编程中,直接引用某些对象会因为种种原因(比如对象创建的开销过大,访问需要安全控制,或者需要跳出当前进程等)带来很多问题,给用户或者系统本身带来不便或者异常。这时,就需要在操作对象(客户端)和被调用对象(真实对象)之间添加一个代理,以协助操作对象控制对被调用对象的访问,起到透明中介的作用。

代理模式的应用:一是远程代理,即为一个对象在不同的地址空间提供局部代表,这样可以隐藏一个对象存在于不同地址空间的事实;二是虚拟代理,即根据需要创建开销很大的对象,通过它来存放实例化需要很长时间的真实对象;三是安全代理,用来控制真实对象访问时的权限;四是智能指引,即当调用真实对象时,代理处理另外一些事。

代理模式的实现,主要有以下几个特点:

1.真实对象和代理对象拥有共同的父类或者实现相同的接口,实现了相同的方法;

2.代理对象引用或者包含真实对象的一个实例,该实例在代理对象中初始化,代理对象通过1中所述的相同方法来替代对真实对象的调用;

3.客户端中不再实例化真实对象,完全通过代理对象来访问真实对象。

代理模式的简单示例,参见如下地址:

http://www.cnblogs.com/kid-li/archive/2006/10/18/532192.html

顺带一提的是,百度百科“java代理模式”词条中的代码示例实际上是装饰模式,而不是代理模式。

在Java中,通常使用Proxy(动态代理)来实现对代理模式的支持。动态代理的详细描述参见《Java核心技术(卷1)》6.5节·代理。

Java中要创建一个代理对象,必须调用Proxy类的静态方法newProxyInstance,该方法的原型如下:

Object Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler) throws IllegalArgumentException

其中:

loader,表示类加载器,对于不同来源(系统库或网络等)的类需要不同的类加载器来加载,这是Java安全模型的一部分。可以使用null来使用默认的加载器;

interfaces,表示接口或对象的数组,它就是前述代理对象和真实对象都必须共有的父类或者接口;

handler,表示调用处理器,它必须是实现了InvocationHandler接口的对象,其作用是定义代理对象中需要执行的具体操作。

InvocationHandler之于Proxy,就如Runnable之于Thread。InvocationHandler接口中只有一个方法invoke,它的作用就跟Runnable中的run方法类似,定义了代理对象在执行真实对象的方法时所希望执行的动作。其原型如下:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

其中:

proxy,表示执行这个方法的代理对象;

method,表示真实对象实际需要执行的方法(关于Method类参见Java的反射机制);

args,表示真实对象实际执行方法时所需的参数。

在实际的编程中,需要优先定义一个实现InvocationHandler接口的调用处理器对象,然后将它作为创建代理类实例的参数。(抑或在调用newProxyInstance方法时使用匿名内部类。)这样就得到了代理对象。

真实对象本身的实例化在调用处理器对象内部完成,实例化时需要的参数也应该及时传入调用处理器对象中。这样一来就完成了代理对象对真实对象的包装,而代理对象需要执行的额外操作也在invoke方法中处理。

其后,在客户端中,如果需要使用真实对象时,就可以用代理对象来替代它了(有时需要类型强制转化)。

下面的示例用Java的动态代理模拟了一个拦截器的功能:对于实现了Action接口的类,如果类名不以Action结尾,则不予执行。

共同接口Action:

public interface Action { public void excute(); }实现了Action接口的三个真实对象:

public class FirstAction implements Action { @Override public void excute() { System.out.println("This is the first action."); } }public class Second implements Action { @Override public void excute() { System.out.println("This is the second action."); } }public class ThirdAction implements Action { @Override public void excute() { System.out.println("This is the third action."); } }调用处理器对象FilterHandler,它是对拦截器的实现:

import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class FilterHandler implements InvocationHandler { private String className; public FilterHandler(String className) { this.className = className; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (className.endsWith("Action")) { System.out.println(className + "." + method.getName()); return method.invoke(Class.forName(className).newInstance(), args); } else { System.out.println(className + " doesn't end with 'Action'"); return null; } } }客户端:

import java.lang.reflect.Proxy; public class Main { public static void main(String[] args) { FilterHandler handler1 = new FilterHandler("com.proxy.filter.FirstAction"); Action first = (Action) Proxy.newProxyInstance(Action.class.getClassLoader(), new Class[] {Action.class}, handler1); FilterHandler handler2 = new FilterHandler("com.proxy.filter.second"); Action second = (Action) Proxy.newProxyInstance(Action.class.getClassLoader(), new Class[] {Action.class}, handler2); FilterHandler handler3 = new FilterHandler("com.proxy.filter.ThirdAction"); Action third = (Action) Proxy.newProxyInstance(Action.class.getClassLoader(), new Class[] {Action.class}, handler3); first.excute(); second.excute(); third.excute(); } }

最后需要特别说明的是:

1.Proxy创建的代理对象实例包含Object类中的全部方法和接口数组(即newProxyInstance方法中的interfaces参数)中的全部方法。但是,代理类会覆盖Object类中的toString、equals和hashCode方法,而clone、getClass等方法则没有重新定义。另外,所有这些方法都需要在InvocationHandler的invoke方法中通过调用Method对象的invoke方法才能真正被执行。

2.对于特定的类加载器和预设的一组接口来说,即便是调用处理器handler不一样,也只能得到同一个代理类。也就是说,示例中三次对newProxyInstance方法的调用,其实只是得到的同一个对象的三个实例而已,并不是三种不同的类。

其它Java动态代理的细节参见《Java核心技术》或Java API。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值