动态代理技术是整个java技术中最重要的一个技术,它是学习java框架的基础,不会动态代理技术,那么在学习Spring这些框架时是学不明白的。java
动态代理技术就是用来产生一个对象的代理对象的。在开发中为何须要为一个对象产生代理对象呢?
举一个现实生活中的例子:歌星或者明星都有一个本身的经纪人,这个经纪人就是他们的代理人,当咱们须要找明星表演时,不能直接找到该明星,只能是找明星的代理人。好比刘德华在现实生活中很是有名,会唱歌,会跳舞,会拍戏,刘德华在没有出名以前,咱们能够直接找他唱歌,跳舞,拍戏,刘德华出名以后,他干的第一件事就是找一个经纪人,这个经纪人就是刘德华的代理人(代理),当咱们须要找刘德华表演时,不能直接找到刘德华了(刘德华说,你找我代理人商谈具体事宜吧!),只能是找刘德华的代理人,所以刘德华这个代理人存在的价值就是拦截咱们对刘德华的直接访问!
这个现实中的例子和咱们在开发中是同样的,咱们在开发中之因此要产生一个对象的代理对象,主要用于拦截对真实业务对象的访问。那么代理对象应该具备什么方法呢?代理对象应该具备和目标对象相同的方法app
因此在这里明确代理对象的两个概念:
一、代理对象存在的价值主要用于拦截对真实业务对象的访问。
二、代理对象应该具备和目标对象(真实业务对象)相同的方法。刘德华(真实业务对象)会唱歌,会跳舞,会拍戏,咱们如今不能直接找他唱歌,跳舞,拍戏了,只能找他的代理人(代理对象)唱歌,跳舞,拍戏,一我的要想成为刘德华的代理人,那么他必须具备和刘德华同样的行为(会唱歌,会跳舞,会拍戏),刘德华有什么方法,他(代理人)就要有什么方法,咱们找刘德华的代理人唱歌,跳舞,拍戏,可是代理人不是真的懂得唱歌,跳舞,拍戏的,真正懂得唱歌,跳舞,拍戏的是刘德华,在现实中的例子就是咱们要找刘德华唱歌,跳舞,拍戏,那么只能先找他的经纪人,交钱给他的经纪人,而后经纪人再让刘德华去唱歌,跳舞,拍戏。框架
如今要生成某一个对象的代理对象,这个代理对象一般也要编写一个类来生成,因此首先要编写用于生成代理对象的类。在java中如何用程序去生成一个对象的代理对象呢,java在JDK1.5以后提供了一个"java.lang.reflect.Proxy"类,经过"Proxy"类提供的一个newProxyInstance方法用来建立一个对象的代理对象,以下所示:ide
1 static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
newProxyInstance方法用来返回一个代理对象,这个方法总共有3个参数,ClassLoader loader用来指明生成代理对象使用哪一个类装载器,Class<?>[] interfaces用来指明生成哪一个对象的代理对象,经过接口指定,InvocationHandler h用来指明产生的这个代理对象要作什么事情。因此咱们只须要调用newProxyInstance方法就能够获得某一个对象的代理对象了。学习
在java中规定,要想产生一个对象的代理对象,那么这个对象必需要有一个接口,因此咱们第一步就是设计这个对象的接口,在接口中定义这个对象所具备的行为(方法)测试
一、定义对象的行为接口编码
1 package cn.gacl.proxy; 2 3 /** 4 * @ClassName: Person 5 * @Description: 定义对象的行为 6 * @author: 孤傲苍狼 7 * @date: 2014-9-14 下午9:44:22 8 * 9 */ 10 public interface Person { 11 12 /** 13 * @Method: sing 14 * @Description: 唱歌 15 * @Anthor:孤傲苍狼 16 * 17 * @param name 18 * @return 19 */ 20 String sing(String name); 21 /** 22 * @Method: sing 23 * @Description: 跳舞 24 * @Anthor:孤傲苍狼 25 * 26 * @param name 27 * @return 28 */ 29 String dance(String name); 30 }
二、定义目标业务对象类spa
1 package cn.gacl.proxy; 2 3 /** 4 * @ClassName: LiuDeHua 5 * @Description: 刘德华实现Person接口,那么刘德华会唱歌和跳舞了 6 * @author: 孤傲苍狼 7 * @date: 2014-9-14 下午9:22:24 8 * 9 */ 10 public class LiuDeHua implements Person { 11 12 public String sing(String name){ 13 System.out.println("刘德华唱"+name+"歌!!"); 14 return "歌唱完了,谢谢你们!"; 15 } 16 17 public String dance(String name){ 18 System.out.println("刘德华跳"+name+"舞!!"); 19 return "舞跳完了,多谢各位观众!"; 20 } 21 }
三、建立生成代理对象的代理类设计
1 package cn.gacl.proxy; 2 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Method; 5 import java.lang.reflect.Proxy; 6 7 /** 8 * @ClassName: LiuDeHuaProxy 9 * @Description: 这个代理类负责生成刘德华的代理人 10 * @author: 孤傲苍狼 11 * @date: 2014-9-14 下午9:50:02 12 * 13 */ 14 public class LiuDeHuaProxy { 15 16 //设计一个类变量记住代理类要代理的目标对象 17 private Person ldh = new LiuDeHua(); 18 19 /** 20 * 设计一个方法生成代理对象 21 * @Method: getProxy 22 * @Description: 这个方法返回刘德华的代理对象:Person person = LiuDeHuaProxy.getProxy();//获得一个代理对象 23 * @Anthor:孤傲苍狼 24 * 25 * @return 某个对象的代理对象 26 */ 27 public Person getProxy() { 28 //使用Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)返回某个对象的代理对象 29 return (Person) Proxy.newProxyInstance(LiuDeHuaProxy.class 30 .getClassLoader(), ldh.getClass().getInterfaces(), 31 new InvocationHandler() { 32 /** 33 * InvocationHandler接口只定义了一个invoke方法,所以对于这样的接口,咱们不用单独去定义一个类来实现该接口, 34 * 而是直接使用一个匿名内部类来实现该接口,new InvocationHandler() {}就是针对InvocationHandler接口的匿名实现类 35 */ 36 /** 37 * 在invoke方法编码指定返回的代理对象干的工做 38 * proxy : 把代理对象本身传递进来 39 * method:把代理对象当前调用的方法传递进来 40 * args:把方法参数传递进来 41 * 42 * 当调用代理对象的person.sing("冰雨");或者 person.dance("江南style");方法时, 43 * 实际上执行的都是invoke方法里面的代码, 44 * 所以咱们能够在invoke方法中使用method.getName()就能够知道当前调用的是代理对象的哪一个方法 45 */ 46 @Override 47 public Object invoke(Object proxy, Method method, 48 Object[] args) throws Throwable { 49 //若是调用的是代理对象的sing方法 50 if (method.getName().equals("sing")) { 51 System.out.println("我是他的经纪人,要找他唱歌得先给十万块钱!!"); 52 //已经给钱了,经纪人本身不会唱歌,就只能找刘德华去唱歌! 53 return method.invoke(ldh, args); //代理对象调用真实目标对象的sing方法去处理用户请求 54 } 55 //若是调用的是代理对象的dance方法 56 if (method.getName().equals("dance")) { 57 System.out.println("我是他的经纪人,要找他跳舞得先给二十万块钱!!"); 58 //已经给钱了,经纪人本身不会唱歌,就只能找刘德华去跳舞! 59 return method.invoke(ldh, args);//代理对象调用真实目标对象的dance方法去处理用户请求 60 } 61 62 return null; 63 } 64 }); 65 } 66 }
测试代码:代理
1 package cn.gacl.proxy; 2 3 public class ProxyTest { 4 5 public static void main(String[] args) { 6 7 LiuDeHuaProxy proxy = new LiuDeHuaProxy(); 8 //得到代理对象 9 Person p = proxy.getProxy(); 10 //调用代理对象的sing方法 11 String retValue = p.sing("冰雨"); 12 System.out.println(retValue); 13 //调用代理对象的dance方法 14 String value = p.dance("江南style"); 15 System.out.println(value); 16 } 17 }
运行结果以下:
Proxy类负责建立代理对象时,若是指定了handler(处理器),那么无论用户调用代理对象的什么方法,该方法都是调用处理器的invoke方法。
因为invoke方法被调用须要三个参数:代理对象、方法、方法的参数,所以无论代理对象哪一个方法调用处理器的invoke方法,都必须把本身所在的对象、本身(调用invoke方法的方法)、方法的参数传递进来。
在动态代理技术里,因为无论用户调用代理对象的什么方法,都是调用开发人员编写的处理器的invoke方法(这至关于invoke方法拦截到了代理对象的方法调用)。而且,开发人员经过invoke方法的参数,还能够在拦截的同时,知道用户调用的是什么方法,所以利用这两个特性,就能够实现一些特殊需求,例如:拦截用户的访问请求,以检查用户是否有访问权限、动态为某个对象添加额外的功能。