动态代理机制
代理一般在编译时无法确定实现哪个接口时才有必要使用。代理类可以在运行时创建全新的类,这样就可以利用代理可以在运行时创建一个实现了一组给定接口的新类。然而不能在运行时定义新类方法的新代码。就需要一个实现接口InvocationbHandler(调用处理器)的类的对象,这个接口只有一个方法,方法签名为:
Object invoke (Object proxy,Method method, Object[] args)
无论何时调用代理对象的方法,调用处理器的invoke方法都会被调用,并向其传递Method对象和原始的调用参数,调用处理器必须给出调用的方式。
一个动态代理机制需要以下几点:
1) 实现接口InvocationHandler(调用处理器)的类,该类实现的唯一方法invoke(),方法签名如下:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
在jdk源码(JDK1.8)中是这样解释这三个参数的:
proxy表示代理的实例,metotd表示声明代理实例上调用的接口方法,args一个对象数组,数组中包含在代理实例中所调用的接口方法中的参数值,或者接口方法没有参数是为null。基本类型的参数要封装在基本类型适当的基本包装器类。例如方法的参数为int封装在Integer类中。
这个方法被一个与代理实例关联的调用处理器调用并返回结果。
2) 代理类Proxy的静态方法newProxyInstance(),该方法签名如下:
public static Object newProxyInstance(ClassLoader loader, Class[] interfaces,InvocationHandler h)
在jdk源码(JDK1.8)中是这样解释这三个参数的:
参数loader定义代理类的类加载器;参数interfaces定义代理类实现的接口列表;h定义调用处理器。
该方法返回一个代理类的实例使调用处理器去调用指定接口的方法。
3) 一个接口。
4) 至少一个实现接口的类让代理器去代理。
一个简单的例子
/**
* 调用处理器
*/
class HandlerTest implements InvocationHandler{
private Object real; //真实实现类的引用
public HandlerTest(Object real){ //通过构造调用处理器将需要代理的实现类引入
this.real = real;
}
@Override
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
System.out.println("调用之前可以先判断辅助条件");
System.out.print(real.getClass().getName());
System.out.print("."+method.getName()+"(");
if(args !=null){
for(int i=0;i<args.length;i++){
System.out.print(args[i]);
if(i<args.length-1){
System.out.print(",");
}
}
}
System.out.println(")");
method.invoke(real, args);
System.out.println("调用之后我们也可以做一些收尾工作");
return null;
}
}
/**
* 接口
*/
interface Call{
void call(String str);
}
/**
* 实现类
*/
class RealCall implements Call{
@Override
public void call(String str) {
System.out.println("我通过代理类代理执行!");
System.out.println(str);
}
}
/**
* 方法newProxyInstance()在主函数中调用
*/
public class ProxyTest2 {
@Test
public void test(){
Call realcall = new RealCall();
InvocationHandler h = new HandlerTest(realcall);
Call proxy = (Call)Proxy.newProxyInstance(h.getClass().getClassLoader(),realcall.getClass().getInterfaces(), h);
System.out.println(proxy.getClass().getName());
proxy.call("代理成功");
}
}
结果如下:
从结果中发现代理实例proxy声明类型为Call,但是实际类型却为ProxyTest.$Proxy4。我们发现代理实例可以调用在关联的调用处理器带入的接口的方法。在main函数中首先创建被代理的类的对象realcall,利用调用处理器的构造函数将改引用加入处理器中,这样调用处理器就可以调用被代类的方法。然后调用Proxy的静态方法,将调用处理器与代理实例关联,这样代理实例就可以调用实际类的方法了。
在调用处理器方法invoke()中利用method.invoke(real, args)调用传进来的方法和参数,real就是传进来的被代理类,agrs[]是参数数组。
代理类的特定与作用
代理类在运行中被创建,但是一旦创建就变成常规类,与其他类无区别,也是超类Object的子类。
代理类都扩展与Proxy类,有个代理类只有一个实例域————调用处理器。
说了那么多代理机制的存在的意义到底是什么?
代理机制可以在不改变接口的前提下,控制对象的访问。就比如一个演员,他的最重要的任务是演好戏,而不是整天合同的细节,与外界的沟通等杂事劳神,这时候就需要经纪人去为他服务,代替他与外界沟通,外界与他联系之前先通过经纪人。代理类就相当于充当着这个角色,把不重要的工作在invoke函数中实现,比如日志,异常,权限检查,再去调用代理方法,再去执行一些善后代码。尽量减轻核心业务代码的与不相关代码的耦合。在没必要的适合不去调用那些复杂的代码,减轻服务器压力。