咱们平时去餐厅吃饭,不是直接告诉厨师作什么菜的,而是先告诉服务员点什么菜,而后由服务员传到给厨师,至关于服务员是厨师的代理,咱们经过代理让厨师炒菜,这就是代理模式。代理模式须要三个东西:
真实对象——厨师(chef),用户真正须要去用到的对象。
调用者——客人(Client),须要用到真实对象的对象。
代理对象——服务员(waiter),代理真实对象与调用者沟通的对象。
厨师须要有个炒菜的方法,服务员负责代理厨师,因此也须要有一个炒菜方法,为了保证两个对象的炒菜方法一致,须要一个接口。java
public interface Cook { public void cook(String name);//传入菜品名字 }
而后chef实现这个接口spring
public class Chef implements Cook{ @Override public void cook(String name) { System.out.println("正在炒"+name); } }
waiter也须要实现这个接口ide
public class Waiter implements Cook{ Chef chef; public Waiter(Chef chef) { //经过构造函数与获取chef的实例对象 this.chef = chef; } @Override public void cook(String name) { //本身不实现作菜,而是调用chef的方法 chef.cook("鱼香肉丝"); } }
设置好后,客人就来点餐了函数
public class Client { public static void main(String[] args) { Chef chef=new Chef(); Waiter waiter=new Waiter(chef); waiter.cook("鱼香肉丝"); } }
这里client没有直接调用chef的cook方法,而是经过waiter调用的cook方法。这样waiter就起到了代理的做用。代理的优势是可让真实对象处理的业务更加纯粹,再也不去关注一些公共的事情,公共的业务由代理来完成。
好比这个点餐的例子,可让厨师专心作饭,不用去管点餐的事情,若是还要增长收钱功能,就可让服务员去完成。测试
public class Waiter implements Cook{ Chef chef; public Waiter(Chef chef) { //经过构造函数与获取chef的实例对象 this.chef = chef; } @Override public void cook(String name) { //本身不实现作菜,而是调用chef的方法 chef.cook("鱼香肉丝"); } public void pay(){ System.out.println("结帐"); } }
可是静态代理的缺点也很明显,多了代理类,工做量变大,开发效率下降。为了弥补这些缺点,就能够使用动态代理。动态代理又有几种实现
基于接口动态代理——jdk动态代理
基于类动态代理——cglib动态代理
还有用javasist来生成动态代理this
先说jdk动态代理,动态代理只须要一个代理类就能够代理全部真实对象。静态代理是这么怎么作的呢?上面例子中,静态代理类(Waiter)代理了真实对象(Chef)。为了保证真实对象的方法(cook)与代理类的方法(cook)一致,因此须要都实现同一个接口(Cook),那若是还须要代理其余类呢?好比代理老板收钱,又须要实现pay接口,代理会计算帐,又须要实现reckon接口。就会很麻烦。代理
动态代理只须要代理类实现一个InvocationHandler接口。就能够在代理类里面动态设置代理对象。API里InvocationHandLer是这样描述的code
InvocationHandler是由代理实例的调用处理程序实现的接口 。 在这个例子中,代理实例就是指服务员(waiter),调用处理程序指调用厨师(chef)的作菜(cook),就是chef.cook("鱼香肉丝")。这里还提到了invoke方法对象
处理代理实例上的方法调用并返回结果。 当在与之关联的代理实例上调用方法时,将在调用处理程序中调用此方法。结合例子,当你想点餐(处理代理实例上的方法)时,就能够调用这个方法。而后结果点餐结果也会告诉你(并返回结果),由于这个方法能够代理不少真实对象,因此返回的Object。
还有一个须要了解的Proxy类,API中的描述blog
注意这里断句,Proxy提供了建立动态代理类和实例,的静态方法。这里的静态方法就是
第一个参数——类加载器,java去执行某一个类的时候,须要将这个.clss文件加载到java虚拟机里面去,这个加载的过程就须要类加载器去加载。 第二个参数——要实现接口的列表,接口也有class。因此是class<?>[]。
第三个参数——这个InvocationHandler就和以前的联系上了。
大概了解后能够配合代码理解了。咱们在上面点餐的例子上更改。动态代理指动态生成的代理类,所以接口(Cook)依然存在,真实对象(Chef)也存在。而后是代理类(waiter),代理类须要去实现InvocationHandler接口,由于能够实现动态代理了,稍微改个名字Waiter2。
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class Waiter2 implements InvocationHandler{ private Cook cook;//真实对象,由于chef实现了Cook,因此建立cook就能够了 public void setCook(Cook cook) { this.cook = cook; } /** *代理方法逻辑 * @param proxy 代理对象(chef) * @param method 当前调度方法,代理对象的调用处理程序方法的对象(cook()) * @param args 当前方法的参数 (菜品名) * @return 返回代理结果 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object obj=method.invoke(cook,args);//第一个参数是真实对象 return obj; } /** * 生成代理类 * @return 返回代理类 */ public Object getProxy(){ return Proxy.newProxyInstance(this.getClass().getClassLoader(),cook.getClass().getInterfaces(),this); } }
详细解释
private Cook cook;//真实对象,由于chef实现了Cook,因此建立cook就能够了 public void setCook(Cook cook) { this.cook = cook; }
代理对象依然由于须要代理真实对象,因此仍是须要先建立真实对象,真实对象厨师Chef继承了接口Cook的烹饪方法cook,因此建立接口Cook就能够了。而后经过set方法来设置真实对象。
/** *代理方法逻辑 * @param proxy 代理对象(chef) * @param method 当前调度方法,代理对象的调用处理程序方法的对象(cook()) * @param args 当前方法的参数 (菜品名) * @return 返回代理结果 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object obj=method.invoke(cook,args);//第一个参数是真实对象 return obj; }
以前提到过,invoke是处理代理实例上的方法调用并返回结果,能够理解成让代理对象与真实对象创建逻辑关系。
这里的method.invoke()是经过反射去调用真实对象,第一个参数cook就是真实对象,第二个参数就是传的参数值。 而后返回代理结果就能够了。
以前还提到过Proxy有一个静态方法能够生成代理类。
/** * 生成代理类 * @return 返回代理类 */ public Object getProxy(){ return Proxy.newProxyInstance(this.getClass().getClassLoader(),cook.getClass().getInterfaces(),this); }
newProxyInstance的第一个参数是类加载器,直接传入当前类的类加载器。
第二参数是接口,这里传入cook的接口。 第三个参数是InvocationHandler,自己这个类就实现了InvocationHandler接口,因此传入当前类。
而后是客户端
public class Client { public static void main(String[] args) { Chef chef=new Chef(); //获取真实对象的实例 Waiter2 waiter2=new Waiter2();//获取代理的实例 waiter2.setCook(chef); //设置代理去代理哪一个真实实例 Cook proxy=(Cook) waiter2.getProxy();//建立代理,getProxy返回的是object,因此转换一下。 proxy.cook("宫保鸡丁");//经过代理去控制真实对象。 } }
Cook proxy=(Cook) waiter2.getProxy();这一行是用来建立代理的,proxy就至关于以前的服务员。建立为Cook类型是为了调用cook方法。以前的静态代理中,用的是Waiter waiter去建立代理,由于Waiter也实现了Cook接口。因此能够用Waiter类型。
运行结果
若是要添加公共方法到代理对象中
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("来了,老弟"); Object obj=method.invoke(cook,args);//第一个参数是真实对象 return obj; }
以前说静态代理有个坏处,有不少类,动态代理能够解决这个问题。如今好像还看不出来。咱们稍微改一下,让动态代理类能够代理全部类。
这个例子中代理的是厨师,也能够加入收钱功能,或者打扫卫生功能。真实对象怎么变,咱们的目的不变,代理真实对象,那么就能够把Waiter中的真实对象Cook,换成Object。Waiter2代码以下
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class Waiter2 implements InvocationHandler{ private Object target;//真实对象 public void setTarget(Object target) { this.target = target; } /** *代理方法逻辑 * @param proxy 代理对象(chef) * @param method 当前调度方法,代理对象的调用处理程序方法的对象(cook()) * @param args 当前方法的参数 (菜品名) * @return 返回代理结果 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("服务员,麻烦你");//公共方法 Object obj=method.invoke(target,args);//第一个参数是真实对象 return obj; } /** * 生成代理类 * @return 返回代理类 */ public Object getProxy(){ return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this); } }
新增两个接口,打扫和付钱,和它们的接口实现类
public interface Clean { public void clean(); }
public class CleanImpl implements Clean{ public void clean(){ System.out.println("打扫"); } }
public interface Pay { public void paymoney(int i); public void free(); }
public class PayImpl implements Pay{ public void paymoney(int i){ System.out.println("付钱诶!一共"+i); } public void free(){ System.out.println("能不能免单"); } }
而后再客户端测试一下
public class Client { public static void main(String[] args) { Chef chef=new Chef(); //获取真实对象的实例 Pay pay=new PayImpl(); Clean clean=new CleanImpl(); Waiter2 waiter2=new Waiter2();//获取代理的实例 waiter2.setTarget(chef); //设置代理去代理哪一个真实实例 Cook cookproxy=(Cook) waiter2.getProxy();//建立代理,getProxy返回的是object,因此转换一下。 cookproxy.cook("宫保鸡丁");//经过代理去控制真实对象。 waiter2.setTarget(pay); Pay payproxy=(Pay) waiter2.getProxy(); payproxy.paymoney(50); payproxy.free(); waiter2.setTarget(clean); Clean cleanproxy=(Clean) waiter2.getProxy(); cleanproxy.clean(); } }
这样这个代理类就能够代理各类各样的类。注意被代理的类必须实现接口,由于在newProxyInstance方法里面第二个参数就是传入真实对象的接口。