@liayun
2016-09-20T08:32:53.000000Z
字数 4913
阅读 1578
java基础加强
动态代理技术在实际开发中用到的非常多,所以有必要详解一下这门技术。这门技术非常难,因此学起来还是比较困难的,但我们不怕困难,定要啃下这块硬骨头。
要想理解动态代理这门技术,必须明确两个概念:
现在要生成某一个对象的代理对象,这个代理对象通常也要编写一个类来生成,所以首先要编写用于生成代理对象的类。那么如何编写生成代理对象的类呢?
要想编写生成代理对象的类,必须考虑到两个要素:
Java提供了一个Proxy类,调用它的newInstance方法可以生成某个对象的代理对象,使用该方法生成代理对象时,需要三个参数:
初学者必须理解,或不理解必须记住的2件事情:
相关概念看完之后,大家可能还是处在云里雾里中,但没关系,我们接下来通过一个例子来领大家入门。
李宇春,网上称春哥(在此处我无意诋毁她),她在还没成名前,一些大老板可能会点她唱歌或跳舞,那么她就会自己去给这些金主唱歌或跳舞,可能是有些许迫不得已吧!但是她成名之后,就会有自己的代理人,即经纪人,一些金主点她唱歌或跳舞时,其代理人就会跳出来,拦截对李宇春的访问,这个时候由代理人出面,若要我代理的对象——李宇春唱歌,必须给1万美刀;若要我代理的对象——李宇春跳舞,必须给2万美刀。这个场景用代码来描述就应该是这样。
首先设计真实的业务对象,即李宇春。
public class Liyuchun {public void sing() {System.out.println("春哥唱歌了!!");}public void dance() {System.out.println("春哥跳舞了!!");}}
接下来我们就要编写生成代理对象的类,首先明确其要代理的对象是李宇春,接着就要设计一个方法生成代理对象(即代理人)。
public class LiyuchunProxy {private Person chunchun = new Liyuchun();public Xxx(返回值) createProxy() {return ...}}
这儿,我们就要思考生成代理对象的方法——createProxy()的返回值的类型应该是什么了?不可能是李宇春,即Liyuchun,那到底应该是什么类型呢?在Java里面有这样一个约定:要想创建某一个对象的代理对象,那么该对象必须实现一个接口。
由于李宇春是一个人,所以我们抽出一个人的接口。
public interface Person {void sing();void dance();}
真实的业务对象(即李宇春)的代码就应修改为:
public class Liyuchun implements Person {@Overridepublic void sing() {System.out.println("春哥唱歌了!!");}@Overridepublic void dance() {System.out.println("春哥跳舞了!!");}}
那这样我们就明确了生成代理对象的方法——createProxy()的返回值的类型了,即为Person,通过该方法产生李宇春的代理,李宇春的代理同样也是一个人,对不对?接下来,我们使用Java提供了的Proxy类,调用它的newInstance方法生成李宇春的代理对象(即代理人),代码大概是这样:
public class LiyuchunProxy {private Person chunchun = new Liyuchun();public Person createProxy() {// 产生某个对象的代理对象return (Person) Proxy.newProxyInstance(LiyuchunProxy.class.getClassLoader(), chunchun.getClass().getInterfaces(), new InvocationHandler() {/** proxy:把代理对象自身传递进来* method:代表当前调用的方法* args:当前调用方法的参数*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {在这里面编写代码来指定产生的代理对象干什么事情...return null;}});}}
假设这样的生成代理对象的类编写好了,有些金主来点春哥唱歌或跳舞,那么其代理人拦截下来之后,就调用代理人自己的唱歌或跳舞方法。这样代码就是这样的:
public class Demo1 {public static void main(String[] args) {LiyuchunProxy proxy = new LiyuchunProxy();Person person = proxy.createProxy();person.sing();person.dance();}}
只要调用代理对象的唱歌、跳舞方法,实际上都是调用invoke方法里面的代码,即invoke方法由唱歌、跳舞方法来调用,当唱歌、跳舞方法来调用invoke方法做事情的时候,会给invoke方法传递一些参数进来。这说明代理对象到底干什么事情是由invoke方法里面的代码来决定的。
这样我们再回过头来彻底编写完生成代理对象的类。
public class LiyuchunProxy {private Person chunchun = new Liyuchun();public Person createProxy() {// 产生某个对象的代理对象return (Person) Proxy.newProxyInstance(LiyuchunProxy.class.getClassLoader(), chunchun.getClass().getInterfaces(), new InvocationHandler() {/** proxy:把代理对象自身传递进来* method:代表当前调用的方法* args:当前调用方法的参数*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 在这里面编写代码来指定产生的代理对象干什么事情String methodName = method.getName();if (methodName.equals("sing")) {System.out.println("拿1万刀来!!!");method.invoke(chunchun, args); // 春春唱歌了} else if (methodName.equals("dance")) {System.out.println("拿2万刀来!!!");method.invoke(chunchun, args); // 春春跳舞了} else {System.out.println("春哥不支持这个功能!!!"); // 比如金主要春哥陪酒,但春哥怎能是那样的人呢!}return null;}});}}
动态代理技术怎可能像上面那样简单呢?动态代理入门之后,我们再来看看还算是比较复杂的动态代理。
金主除了点春哥唱歌之外,还要指定她专门唱哪首歌,春哥唱完歌之后还要对金主说一声谢谢;他们除了点春哥跳舞之外,还要指定她专门跳什么舞,春哥跳完舞之后还得给金主们一个飞吻!在这样的场景下,Person接口的代码应修改为:
public interface Person {String sing(String name);String dance(String name);}
真实业务对象(即李宇春)的代码应修改为:
public class Liyuchun implements Person {@Overridepublic String sing(String name) {System.out.println("春哥唱"+name+"歌了!!");return "谢谢哟!!!";}@Overridepublic String dance(String name) {System.out.println("春哥跳"+name+"舞了!!");return "飞吻!!!";}}
生成代理对象的类的代码应修改为:
public class LiyuchunProxy {private Person chunchun = new Liyuchun();public Person createProxy() {// 产生某个对象的代理对象return (Person) Proxy.newProxyInstance(LiyuchunProxy.class.getClassLoader(), chunchun.getClass().getInterfaces(), new InvocationHandler() {/** proxy:把代理对象自身传递进来* method:代表当前调用的方法* args:当前调用方法的参数*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 在这里面编写代码来指定产生的代理对象干什么事情String methodName = method.getName();if (methodName.equals("sing")) {System.out.println("拿1万刀来!!!");/** 春春要唱歌了,唱完歌还要对金主们说声:谢谢哟!!!* 由于是代理对象拦截了对真实业务对象的访问,所以若是不将"谢谢哟!!!"返回,* 结果就是其返回给代理对象了,金主们肯定要不爽了。*/return method.invoke(chunchun, args);} else if (methodName.equals("dance")) {System.out.println("拿2万刀来!!!");/** 春春要跳舞了,跳完舞还要给金主们一个飞吻!!!* 同样,若是不将"飞吻!!!"返回,* 结果就是其返回给代理对象了,金主们肯定要日娘了。*/return method.invoke(chunchun, args);} else {System.out.println("春哥不支持这个功能!!!"); // 比如金主要春哥陪酒,但春哥怎能是那样的人呢!}return null;}});}}
这样,金主要点春哥唱《江南皮革厂》,并且还要我们的春哥跳钢管舞,那么代码就该是这样的:
public class Demo {public static void main(String[] args) {LiyuchunProxy proxy = new LiyuchunProxy();Person person = proxy.createProxy();String result = person.sing("江南皮革厂");System.out.println(result);result = person.dance("钢管舞");System.out.println(result);}}