目标:
掌握代理机制的作用
掌握InvocationHandler接口,和Proxy类的作用。
具体内容
代理设计,一个操作的接口有两个子类,其中一个是真实主题的实现类。另一个是代理实现类。
可以复习之前的代理设计模式:
接口与抽象类的应用(包括各自设计模式)
代理实现类需要完成比真实主题实现类更多的内容,而且本身还需要处理一些与具体业务有关的程序代码。
普通代理实例:
package 类集; interface Subject{ public String say(String name,int age) ; // 定义抽象方法say } class RealSubject implements Subject{ // 实现接口 public String say(String name,int age){ return "姓名:" + name + ",年龄:" + age ; } }; class ProxySubject implements Subject{ Subject b=null; public ProxySubject(Subject b) { this.b=b; } public String say(String name,int age) { return this.b.say(name, age); } } public class GetInterfaceDemo { public static void main(String[] args) throws Exception { Subject b1=new ProxySubject(new RealSubject()); System.out.print(b1.say("小红", 23)); } }
以上代理操作实际被称为静态代理,因为一个代理类,只能为一个接口服务,那么如果有很多接口,则代理类很多。
而且,所有代理操作除了调用的方法不一样,其他操作都一样,则此时肯定重复代码了。
InvocationHandler接口
接口:
public interface InvocationHandler
接口中方法:
Object invoke(Object proxy, Method method, Object[] args)
在代理实例上处理方法调用并返回结果。
proxy
- 被代理的对象
method
- 要调用的方法。
args
- 方法调用时所需参数
可以将InvocationHandler接口的子类想象成一个代理的最终操作类,替换掉ProxySubject。如下:
class MyInvocationHandler implements InvocationHandler{ public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{ } };
Proxy类
Proxy类是专门的完成代理的操作类,可以通过此类为一个或多个接口动态的实现类,此类提供以下操作方法。
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
参数说明:
ClassLoader loader:类加载器
interfaces:得到全部的接口
h:得到InvocationHandler接口的子类实例。
ClassLoader表示类的加载器,对于java类来说,类加载器主要有三种。
1)Bootstrap ClassLoader2)Extention ClassLoader3)AppClassLoader:加载classpath指定的类,是最常用的加载器。
如果要得到一个加载器对象,肯定使用Class类完成。
取得类加载器实例:
package 类集; class Person{}; public class GetInterfaceDemo { public static void main(String[] args) throws Exception { Person stu = new Person() ;
//相当于通过一个Class类对象获取类加载器,然后对这个类加载器,获取它的Class类对象,再获取这个类对象名称 System.out.println("类加载器:" + stu.getClass().getClassLoader().getClass().getName()) ; } }
输出结果:
类加载器:sun.misc.Launcher$AppClassLoader
动态代理实例:
package 类集; import java.lang.reflect.Array; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; interface Subject{ //定义一个接口 public String say(String name,int age) ; // 定义抽象方法say } class RealSubject implements Subject{ // 实现接口 public String say(String name,int age){ return "姓名:" + name + ",年龄:" + age ; } }; class MyInvocationHandler implements InvocationHandler{ private Object obj ; //真实主题 public Object bind(Object obj){ //绑定真实操作主题 this.obj = obj ; return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this) ;//取得代理对象 } public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{ //动态调用方法 this.before();
Object temp = method.invoke(this.obj,args) ; // 调用方法,传人真实主题和参数 return temp ; //返回方法的返回信息 }
public void before()
{
System.out.Println("代理之前");
}
}; public class GetInterfaceDemo { public static void main(String[] args) throws Exception { Subject sub = (Subject)new MyInvocationHandler().bind(new RealSubject()) ;//通过bind()方法返回一个Proxy对象给sub,并且需要转换类型。 String info = sub.say("小华",30) ; System.out.println(info) ; } }
输出结果:
代理之前
姓名:小华,年龄:30
代码讲解:
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{ //动态调用方法 Object temp = method.invoke(this.obj,args) ; // 调用方法,传人真实主题和参数 return temp ; //返回方法的返回信息 }
这个方法是在真实主题对象调用方法的时候自动调用。
至于method.invoke(),复习前面知识。
执行调用的方法:Method类中的invoke();
Object invoke(Object obj, Object... args) 对带有指定参数的指定对象调用由此 Method 对象表示的底层方法
里面的this.obj就是RealSubject的实例对象,args就是调用say()方法时候传人的参数:小华,30。
里面的method,调用哪个方法,这个method就表示哪个方法,因此他invoke里面的参数就带什么。
比如这里调用了say(String name,int age),相当于做了以下操作:
Method met = obj.getClass().getMethod("sayHello",String.class,int.class) ;
met.invoke(c1.newInstance(),"小华",30) ; // 调用方法
发现,使用动态代理,就可以完成代理功能,而且可以代理全部接口。