您的位置:首页 > 编程语言 > Java开发

代理模式ProxyPattern以及java对此的支持——动态代理

2013-09-03 17:06 603 查看
代理模式,即Proxy Pattern,23种java常用设计模式之一。代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。通俗的说,就是客户端通过代理对象去操纵委托对象(或者说被代理对象)。

举个例子,就业季到了,你需要定制一套西服,打扮一番,人模人样

给面试官留个好印象。通常校园里贴小广告为同学定制西服的并不是裁缝铺而是中介或者说是裁缝铺的代理,按照小广告上留的地址你找到了这家高端大气上档次西服代理,“请问您有什么需求?“代理满脸欢喜,这时你就会洋气地告诉代理:我想要一套高大上的西服,速速给爷做好,钱不是问题~~

代理爽快答应,量了身材尺寸并让你预交一定量定金后就让你回去了,”帅哥,三天之后来取,包您满意!

“。然后代理会转告幕后的裁缝铺:做一套高大上的西服,尺寸在这,三天交货,速度!裁缝铺接到这单业务便会连夜赶制,三天后交到代理那里。代理约你到店里,交付衣服,收取货款。至此,代理模式就完成了,裁缝铺拥有做西服的能力,但它并不直接与客户打交道,而是委托代理接洽生意。通过代理,客户不仅可以间接让裁缝铺做西服还可以在代理那里做一些其他的事情,比如谈价格、交定金、试衣服、交货款等等(我们假设裁缝铺没有这些能力,比如裁缝是个哑巴

不会讲话没法跟你谈价格)。

下面我们用代码实现代理模式:

代理模式中,委托类和代理类实现同一个接口,取名叫Seller:

package proxypattern;

//代理模式中委托类和代理类统一实现的接口
public interface Seller {
// 接口函数:做一套西服
public void workOnSuite();
}
委托类——裁缝类Tailor,实现Seller接口:
package proxypattern;

public class Tailor implements Seller {

@Override
public void workOnSuite() {
// TODO Auto-generated method stub
System.out.println("我是裁缝,我正在做一套西服。");
}

}
代理类Agency,同样实现Seller接口:

package proxypattern;

public class Agency implements Seller {

private Tailor tailor; // 中介持有裁缝对象的引用,通过此引用让裁缝做西服

public Agency(Tailor tailor) {
this.tailor = tailor;
}

@Override
public void workOnSuite() {
// TODO Auto-generated method stub
System.out.println("我是中介,我让裁缝做西服。");

doSomethingBefore();

tailor.workOnSuite();

doSomethingAfter();
}

private void doSomethingAfter() {
// TODO Auto-generated method stub
System.out.println("在给客户西服前,让他试试合身不,把没交的钱补齐");
}

private void doSomethingBefore() {
// TODO Auto-generated method stub
System.out.println("在让裁缝做西服前,谈价格、量尺寸、交定金");
}

}

下面通过客户端完成对代理模式的完整测试:

package proxypattern;

public class Test {

public static void main(String[] args) {
Tailor tailor = new Tailor(); // 裁缝对象
Agency agency = new Agency(tailor); // 代理对象

agency.workOnSuite(); // 让代理做一套西服,代理会在内部让裁缝做西服
}

}
输出:

我是中介,我让裁缝做西服。
在让裁缝做西服前,谈价格、量尺寸、交定金
我是裁缝,我正在做一套西服。
在给客户西服前,让他试试合身不,把没交的钱补齐


可以看到,通过代理模式,用户不仅可以让委托类完成工作外,还可以在代理类中做一些委托类不能完成的任务,这就是Java Web编程中拦截器的概念~面向切面编程AOP。

并且代理和委托类实现同一个接口,让他们具有相同的 对外接口~

上面这种代理模式叫做静态代理模式,java提供了对代理模式的更高级支持——动态代理模式。

动态代理模式主要涉及到java.lang.reflect包中的Proxy静态类以及InvocationHandler接口,具体用法请参考API文档。

动态代理是很多框架和技术的基础,Spring的AOP实现就是基于动态代理的。了解动态代理的机制对于理解AOP的底层实现很有帮助。

Proxy静态类用于创建代理类,这个代理类$Proxy0是Proxy的子类且实现了目标对象的接口。代理类对象实现了代理目标的所有接口,并代替目标对象进行实际的操作。但这种代替不是一种简单的代替,这样没有任何意义,代理的目的是在目标对象方法的基础上作增强,这种增强的本质通常就是对目标对象的方法进行拦截。所以,代理类应该包括一个方法拦截器,来指示当拦截到方法调用时作何种处理。InvocationHandler就是拦截器的接口。

InvocationHandler接口中声明的方法为:Object invoke(Object proxy,Method method,Object[] args);

这个接口有三个参数,后两个比较容易理解,method是被拦截的方法,args是该方法的参数列表。关键是第一个参数,由javadoc可知它是一个代理实例并且它是一个实现了目标对象接口的对象,而不同于目标对象。也就是说代理机制是面向接口的。

首先依然是Seller接口和Tailor委托类(代码见上)。
然后是实现InvocationHandler接口的拦截器类。

package proxypattern;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class Interceptor implements InvocationHandler {

private Tailor tailor; // 拦截器中存放tailor引用,用以操作tailor

public Interceptor(Seller tailor) {
this.tailor = (Tailor) tailor;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
System.out.println("我是代理,拦截到让裁缝做西服的请求。");
doSomethingBefore(); // 在执行method前干点儿什么事儿

method.invoke(tailor, args); // 拦截到method

doSomethingAfter(); // 在执行完method后也可以干点儿事儿
return null;
}

private void doSomethingAfter() {
// TODO Auto-generated method stub
System.out.println("我是代理,在裁缝做完西服,做点儿额外的事儿,试衣服,交剩下的钱。");
}

private void doSomethingBefore() {
// TODO Auto-generated method stub
System.out.println("我是代理,在让裁缝做西服前,做点儿额外的事儿:谈价格、交定金。");
}

}
最后就可以用Proxy静态类创建代理对象了,不清楚的地方仔细看看代码中的注释。

package proxypattern;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Client {

/**
* @param args
* @throws Throwable
* @throws NoSuchMethodException
*/
public static void main(String[] args) throws Throwable {
// TODO Auto-generated method stub
Tailor tailor = new Tailor();
// invocationhandler实现类,里面包含了委托类的引用
InvocationHandler handler = new Interceptor(tailor);

/**
* 获得代理类($Proxy0 extends Proxy implements Seller)的实例
*/
// 生成一个代理类的Class对象
Class cls = Proxy.getProxyClass(tailor.getClass().getClassLoader(),
tailor.getClass().getInterfaces());
// /获得代理类的构造函数,便于下一步构造代理类对象。
// 由于代理类是继承了Proxy的,这里获取继承的构造函数:protected Proxy(InvocationHandler h)
Constructor constructor = cls
.getConstructor(new Class[] { InvocationHandler.class });
// 得到构造函数后就可以构造代理类对象啦~~
Seller seller = (Seller) constructor
.newInstance(new Object[] { handler });
seller.workOnSuite();

// // 或者一步到位,直接得到代理类对象
// seller = (Seller) Proxy.newProxyInstance(tailor.getClass()
// .getClassLoader(), tailor.getClass().getInterfaces(), handler);
// // 代理类是继承自委托类的,所以调用代理类的request函数,就执行了委托类的request函数。
// seller.workOnSuite();
}

}

输出:

我是代理,拦截到让裁缝做西服的请求。
我是代理,在让裁缝做西服前,做点儿额外的事儿:谈价格、交定金。
我是裁缝,我正在做一套西服。
我是代理,在裁缝做完西服,做点儿额外的事儿,试衣服,交剩下的钱。


可以把上面代码中最后部分的注释去了,等价于它上面创建动态代理对象的过程,只是更简洁,一步到位,结果是一样的。

有两篇文章个人认为对于深入理解java动态代理很有价值,在这里也感谢作者的分享:

第一篇:JDK的动态代理深入解析(Proxy,InvocationHandler)

第二篇:java 动态代理深度学习(Proxy,InvocationHandler),含$Proxy0源码
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: