代理(Porxy)模式是一种常用的设计模式.
代理模式最主要的目的,原有的类对象由于某种原因不能访问,需要通过一个新的类来间接地去实现,这个新的类就称为代理类.
代理模式给某一个对象提供一个代理对象,并由代理对象控制对源对象的引用。
代理就是一个人或一个机构代表另一个人或者一个机构采取行动。某些情况下,客户不想或者不能够直接引用一个对象,
代理对象可以在客户和目标对象直接起到中介的作用。客户端分辨不出代理主题对象与真实主题对象。
代理模式可以并不知道真正的被代理对象,而仅仅持有一个被代理对象的接口,这时候代理对象不能够创建被代理对象,
被代理对象必须有系统的其他角色代为创建并传入。
J2EE里的代理模式通常采用更强大的对象类替换目标对象.
以便在目标对象的方法前后执行我们希望执行的代码
例如,对于普通的业务逻辑组件,其方法应该有事务性,但这种开始事务和结束事务都是通用步骤,
因此原始业务逻辑对象的方法无须事务操作,而是由系统生成动态代理来负责事务操作,并调用实际的目标方法
1.静态代理(自定义代理类):
【第1步】定义接口
public interface Service
{
public void method1();
public void method2();
}
============================================================
【第2步】实现接口
public class ServiceImpl implements Service
{
public void method1()
{
System.out.println("执行业务方法1");
}
public void method2()
{
System.out.println("执行业务方法2");
}
}
============================================================
【第3步】实现代理类★★
public class Supply implements Service{
private Service im;
public Supply(Service im)//★★构造方法,参数是目标对象
{
this.im=im;
}
public void method1()
{
System.out.println("给method1方法添加代理逻辑... ... ...");//★★此处将公用的代码实现
im.method1();
... ...
}
public void method2()
{
System.out.println("给method2方法添加代理逻辑... ... ...");
im.method2();
... ...
}
}
============================================================
【第4步】运用代理
public class test {
public static void main(String []s)
{
Service target=new ServiceImpl();//创建目标类
Service proxy=new Supply(target);//创建代理类,将目标类作为参数进行传递
proxy.method1();
proxy.method2();//通过代理类来调用方法
}
}
说明:
Spring的AOP底层就是用代理实现的。通过代理,可将公用的功能 "植入"方法中,
现看第四步的主方法,首先创建目标类对象,也就是要被 "植入"公用功能的类对象。
再创建代理类对象,在代理类中分别将方法重写了,在重写的过程中将公用功能"植入"到程序中。
最后通过代理类调用方法就行了。
举个例子说明,老王买/卖鱼的例子
public class SellFisher{
public int sellFish(){
System.out.println("my fish is delicious!!");
return 10;
}
}
这是一个具体的卖鱼类,表示鱼10元/斤,如果这个类被用到系统中的时候,系统应对变化的灵活性就会大打折扣,请看如下:
public class SellFishSystem{
private SellFisher sellfish=new SellFisher();
//...
public void sellFish(){
sellfish.sellFish();
}
}
如果以后鱼的价格变化,或者具体的卖鱼方法发生变化,就必须修改已经有的SellFisher的sellFish()的代码,这个情况
使得系统的可扩展性降低,我们肯定会想到解决方案了,定义一个接口,请看代码:
interface SellFisher{
int sellFish();
}
public class SellFishSystem {
private SellFisher sellfish;
public void sellFish(){
sellfish.sellFish();
}
}
如果要卖鱼,可以新增加一个子类,新增加一个类的代码风险要大大低于对已有类的代码的修改.
接上面的例子来解释Proxy的模式就简单多了,比如现在鱼的价格变化,卖鱼的提示也发生变化了,我们需要一个新的Proxy类来实现
interface SellFisher {
int sellFish();
}
public class ConcreteSellFisher implements SellFisher {
public int sellFish() {
System.out.println("my fish is delicious!!");
return 10;
}
}
public class ProxySellFisher implements SellFisher //运用代理{
private SellFisher sell;
public ProxySellFisher(SellFisher sell) {
this.sell = sell;
}
public int sellFish() {
System.out.println("the fish price higher");
//编写公用的功能
return sell.sellFish()+10;
}
}
public class ClientTest {
public static void main(String args[]) {
SellFisher s = new ConcreteSellFisher();
ProxySellFisher ps=new ProxySellFisher(s);
ps.sellfish();
}
}
看上面,这个是Proxy模式的代码例子实现,我们现在在SellFishSystem 使用ProxySellFisher来卖鱼了,
由ProxySellFisher再调用原来的ConcreteSellFisher类.具体的一些特征总结为:
1.有个代理类Proxy(和原来的实现类继承同一接口),该类里引用了原来的具体功能实现类(这里是ConcreteSellFisher)
2.重写实现方法,加一些新的变化的元素(比如鱼的价格上涨)
代理模式另一个例子.我们要卖房子,可以有多种方法,通过中介公司卖,中介公司要收一定的手续费.中介公司就相当于代理.
========================================================================================
2.动态代理(自定义代理类):
JDK里的Proxy类也实现了这个模式,只不过它叫动态代理,因为它的代理类变成了InvocationHandler了,执行的方法是invoke了,
从而变得更加灵活了(第一步与第二步都一样)
【第1步】定义接口
public interface Service
{
public void method1();
public void method2();
}
============================================================
【第2步】实现接口
public class ServiceImpl implements Service
{
public void method1()
{
System.out.println("执行业务方法1");
}
public void method2()
{
System.out.println("执行业务方法2");
}
}
============================================================
【第3步】实现代理类★★
import java.lang.reflect.*;//★★导包
import java.util.*;
public class ProxyHandler implements InvocationHandler//★★实现InvocationHandler接口
{
private Object target;//★★定义目标类(这里用的是Object类去引用)
public void setTarget(Object o)
{
this.target = o;
}
public Object invoke(Object proxy, Method method, Object[] args)throws Exception//★★实现接口中的方法
//第二个参数是方法的名称,可以通过method.getName()获取方法名
//第三个参数是方法中的参数,是一个数组
{
Object result = null;
if (method.getName().equals("method1"))//判断方法名是否是 "method1"
{
System.out.println("======开始事务=======");
result =method.invoke(target, args);//★★★★调用目标对象中的方法,第一个参数是目标类实例对象,第二参数是方法中的参数
System.out.println("======提交事务=======");
}
else
{
result =method.invoke(target, args);
}
return result;
}
}
============================================================
【第4步】将接口与代理类相关联★★
import java.lang.reflect.*;
public class MyProxyFactory
{
public static Object getProxy(Object object)//定义方法,参数是目标对象
{
ProxyHandler handler = new ProxyHandler();//创建代理类对象
handler.setTarget(object);设置托管对象的目标对象
return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),handler);★★★★
//通过Proxy类的静态方法进行动态代理,返回代理后的类对象,但是Object类型
//第一个参数是用来创建 动态代理 的ClassLoader对。
//第二个参数是目标对象的接口
//第三个参数是代理类的实体类
}
}
============================================================
【第5步】运用代理
public class TestService
{
public static void main(String[] args)
{
Service service = null;
Service targetObject = new ServiceImpl();//创建目标类对象
Object proxy = MyProxyFactory.getProxy(targetObject);//通过动态代理类获取
if (proxy instanceof Service)//判断返回的对象是否是接口的实例对象
{
service = (Service)proxy;//强转
}
service.method1();//调用代理后的方法
service.method2();
}
}
j2ee在1.3以后提供了Proxy、InvocationHandler来支持代理模式。对于java内建的代理来说,就是client不直接访问对象,
而是通过Proxy对象来访问,而在访问实际对象前后我们可以自己定义一些其他的操作。具体来讲,
client访问的对象通过Proxy.newProxyInstance()给出,client要访问的实际的类可以通过Proxy.getProxyClass获得,
而实际的调用将访问到我们实现的 InvocationHandler对象的invoke()方法中来。
在invoke()方法中我们可以为实际的调用添加额外的代码以满足不同的需要。
现在还是用买鱼的例子:
import java.lang.reflect.*;
import java.util.*;
interface SellFisher {
int sellFish();
}
class ConcreteSellFisher implements SellFisher {
public int sellFish() {
System.out.println("my fish is delicious!!");
return 10;
}
}
class ProxySellFisher implements InvocationHandler {
private SellFisher sell;
public ProxySellFisher(SellFisher sell) {
this.sell = sell;
}
public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
System.out.println("请登记");
return (Integer)method.invoke(sell, args);
}
}
class ClientTest {
public static void main(String args[]) {
SellFisher s = new ConcreteSellFisher();
InvocationHandler p = new ProxySellFisher(s);
Object obj = Proxy.newProxyInstance(s.getClass().getClassLoader(), s.getClass().getInterfaces(), p);
((SellFisher)obj).sellFish();
}
}
请注意,invoke(Object obj,Method method,Object[] args),这里的第一个参数obj其实可以看作没有用处的,
不知道jdk为什么要把它也当作一个参数放这里,methd.invoke()方法,需要把原来的具体实现类(目标类)作为参数传递进去,
method.invoke(obj,args)相当于obj.method(args)
总结,从设计模式的角度讲,大家以后编程中,尽量要面向接口,更通俗一点就是,一个类中使用的别的对象成员变量,
最好定义成接口的变量而不是实际实现类的变量
代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,
一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。