说到代理首先想到的是代理卖东西,比如你帮某个衣服品牌代理卖衣服,客户告诉你选中的衣服款式,你再转告给卖家,实际上发货和衣服还是在卖家那里,你在中间只是做一个前置和后置,卖家发货了,买家如果需要售后服务还是会先联系你,由你转告卖家处理。
那么java上为什么会用到代理模式呢?再说一个例子:你的项目中有一个商店的类,这个商店在初期时候被定义只有两个方法:买和卖。现在项目越做越大,需求商店有搜索等功能,那么这时候应该怎么办呢?
想到的方法有:1、修改这个商店类,给它加上搜索等功能;
2、写个子类继承它,子类中加上搜索等功能;
以上方法都可以,但是和代理并没有什么关系,为什么要举上面的例子呢?是为了对比区分真正需要代理的情况。
注意区分:还是那个原来的商店类,新的需求是需要在“买“的方法中先判断商品是否还有剩余、顾客金钱是否足够支付等;和”卖“中先判断这个商品是否过期等。这个时候能想到办法有:
1、修改这个商店类里的“买”和“卖”方法,加上前置或者后置的需求;
2、写子类覆写“买”和“卖”的方法。
3、代理。
以上两个方法都可以,但是如果这个类不是你写的,或者是第三方jar包,那么修改起来的成本可能会非常大。这时候我们就需要用到代理模式。
第一种:静态代理
有一个商店类,功能单一,只有买和卖两个方法。
public class Shop { public void buy(String shopName){ // } public void sell(String shopName){ // } }
代理商店类,给买和卖方法加入一些前置或者后置的操作:
public class AgentShop { private Shop shop = new Shop(); public void buy(String shopName){ //假如 shopName = 可乐;检查可乐是否还有库存 shop.buy(shopName); } public void sell(String shopName){ //假如 shopName = 雪碧; 检查雪碧是否合法 shop.sell(shopName); } }
从上面可以看出来,每一个代理类只能服务一个类或者接口,代码很多,类很多,有没有更优化的方法呢,java提供了一种动态代理机制,可以动态生成一个代理类。
第二种:动态代理
还是有一个功能单一的商店,这次不是类换成接口演示:
public interface IShop { public void buy(String shopName); public void sell(String shopName); }
写一个子类:
public class NewShop implements IShop { @Override public void buy(String shopName) { } @Override public void sell(String shopName) { } }
使用java提供的InvocationHandler接口:
/** * 代理商店 实现InvocationHandler接口 * * */ public class DynamicAgentShop implements InvocationHandler { private Object mTarget; /** * DOC 通过反射动态生成一个代理对象,调用该代理对象中的任何方法,都会进入invoke方法中。 * @param target * @return */ public Object bind(Object target){ this.mTarget = target; return java.lang.reflect.Proxy.newProxyInstance(mTarget.getClass().getClassLoader(), mTarget.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; //这里写前置 result = method.invoke(mTarget, args); //这里写后置 return result; } }
测试的写法:
private void testDynamicAgent() { DynamicAgentShop dynamicAgentShop =new DynamicAgentShop(); IShop newShop = (IShop) dynamicAgentShop.bind(new NewShop()); newShop.buy("可乐"); }