十分钟理解Java中的动态代理

十分钟帮助你们理解Java中的动态代理,什么是动态代理?感兴趣的小伙伴们能够参考一下
 

若代理类在程序运行前就已经存在,那么这种代理方式被成为 静态代理 ,这种状况下的代理类一般都是咱们在Java代码中定义的。 一般状况下, 静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类。java

1、概述
1. 什么是代理
咱们你们都知道微商代理,简单地说就是代替厂家卖商品,厂家“委托”代理为其销售商品。关于微商代理,首先咱们从他们那里买东西时一般不知道背后的厂家到底是谁,也就是说,“委托者”对咱们来讲是不可见的;其次,微商代理主要以朋友圈的人为目标客户,这就至关于为厂家作了一次对客户群体的“过滤”。咱们把微商代理和厂家进一步抽象,前者可抽象为代理类,后者可抽象为委托类(被代理类)。经过使用代理,一般有两个优势,而且可以分别与咱们提到的微商代理的两个特色对应起来:
优势一:能够隐藏委托类的实现;
优势二:能够实现客户与委托类间的解耦,在不修改委托类代码的状况下可以作一些额外的处理。
2. 静态代理
若代理类在程序运行前就已经存在,那么这种代理方式被成为 静态代理 ,这种状况下的代理类一般都是咱们在Java代码中定义的。 一般状况下, 静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类。 下面咱们用Vendor类表明生产厂家,BusinessAgent类表明微商代理,来介绍下静态代理的简单实现,委托类和代理类都实现了Sell接口,Sell接口的定义以下:ide

public interface Sell { void sell(); void ad(); 
 
} 
Vendor类的定义以下:
public class Vendor implements Sell { public void sell() { 
 
System.out.println("In sell method"); 
 
} public void ad() { 
 
System,out.println("ad method") 
 
} 
 
}

代理类BusinessAgent的定义以下:函数

public class Vendor implements Sell { public void sell() { 
 
System.out.println("In sell method"); 
 
} public void ad() { 
 
System,out.println("ad method") 
 
} 
 
}

从BusinessAgent类的定义咱们能够了解到,静态代理能够经过聚合来实现,让代理类持有一个委托类的引用便可。
下面咱们考虑一下这个需求:给Vendor类增长一个过滤功能,只卖货给大学生。经过静态代理,咱们无需修改Vendor类的代码就能够实现,只需在BusinessAgent类中的sell方法中添加一个判断便可以下所示:this

public class BusinessAgent implements Sell { 
 
... 
 
public void sell() { 
 
if (isCollegeStudent()) { 
 
vendor.sell(); 
 
} 
 
} 
 
... 
 
}

这对应着咱们上面提到的使用代理的第二个优势:能够实现客户与委托类间的解耦,在不修改委托类代码的状况下可以作一些额外的处理。静态代理的局限在于运行前必须编写好代理类,下面咱们重点来介绍下运行时生成代理类的动态代理方式。spa

2、动态代理
1. 什么是动态代理
代理类在程序运行时建立的代理方式被成为 动态代理。 也就是说,这种状况下,代理类并非在Java代码中定义的,而是在运行时根据咱们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优点在于能够很方便的对代理类的函数进行统一的处理,而不用修改每一个代理类的函数。 这么说比较抽象,下面咱们结合一个实例来介绍一下动态代理的这个优点是怎么体现的。
如今,假设咱们要实现这样一个需求:在执行委托类中的方法以前输出“before”,在执行完毕后输出“after”。咱们仍是以上面例子中的Vendor类做为委托类,BusinessAgent类做为代理类来进行介绍。首先咱们来使用静态代理来实现这一需求,相关代码以下:代理

public class BusinessAgent implements Sell { 
 
private Vendor mVendor; 
 
public BusinessAgent(Vendor vendor) { 
 
this.mVendor = vendor; 
 
} 
 
public void sell() { 
 
System.out.println("before"); 
 
mVendor.sell(); 
 
System.out.println("after"); 
 
} 
 
public void ad() { 
 
System.out.println("before"); 
 
mVendor.ad(); 
 
System.out.println("after"); 
 
} 
 
}

从以上代码中咱们能够了解到,经过静态代理实现咱们的需求须要咱们在每一个方法中都添加相应的逻辑,这里只存在两个方法因此工做量还不算大,假如Sell接口中包含上百个方法呢?这时候使用静态代理就会编写许多冗余代码。经过使用动态代理,咱们能够作一个“统一指示”,从而对全部代理类的方法进行统一处理,而不用逐一修改每一个方法。下面咱们来具体介绍下如何使用动态代理方式实现咱们的需求。
2. 使用动态代理
(1)InvocationHandler接口
在使用动态代理时,咱们须要定义一个位于代理类与委托类之间的中介类,这个中介类被要求实现InvocationHandler接口,这个接口的定义以下:code

public interface InvocationHandler { 
 
Object invoke(Object proxy, Method method, Object[] args); 
 
}

从InvocationHandler这个名称咱们就能够知道,实现了这个接口的中介类用作“调用处理器”。当咱们调用代理类对象的方法时,这个“调用”会转送到invoke方法中,代理类对象做为proxy参数传入,参数method标识了咱们具体调用的是代理类的哪一个方法,args为这个方法的参数。这样一来,咱们对代理类中的全部方法的调用都会变为对invoke的调用,这样咱们能够在invoke方法中添加统一的处理逻辑(也能够根据method参数对不一样的代理类方法作不一样的处理)。所以咱们只需在中介类的invoke方法实现中输出“before”,而后调用委托类的invoke方法,再输出“after”。下面咱们来一步一步具体实现它。
(2)委托类的定义
动态代理方式下,要求委托类必须实现某个接口,这里咱们实现的是Sell接口。委托类Vendor类的定义以下:对象

public class Vendor implements Sell { 
 
public void sell() { 
 
System.out.println("In sell method"); 
 
} 
 
public void ad() { 
 
System,out.println("ad method") 
 
} 
 
}

(3)中介类
上面咱们提到过,中介类必须实现InvocationHandler接口,做为调用处理器”拦截“对代理类方法的调用。中介类的定义以下:接口

public class DynamicProxy implements InvocationHandler { 
 
private Object obj; //obj为委托类对象; 
 
public DynamicProxy(Object obj) { 
 
this.obj = obj; 
 
} 
 
@Override 
 
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
 
System.out.println("before"); 
 
Object result = method.invoke(obj, args); 
 
System.out.println("after"); 
 
return result; 
 
} 
 
}

从以上代码中咱们能够看到,中介类持有一个委托类对象引用,在invoke方法中调用了委托类对象的相应方法(第11行),看到这里是否是以为似曾相识?经过聚合方式持有委托类对象引用,把外部对invoke的调用最终都转为对委托类对象的调用。这不就是咱们上面介绍的静态代理的一种实现方式吗?实际上,中介类与委托类构成了静态代理关系,在这个关系中,中介类是代理类,委托类就是委托类; 代理类与中介类也构成一个静态代理关系,在这个关系中,中介类是委托类,代理类是代理类。也就是说,动态代理关系由两组静态代理关系组成,这就是动态代理的原理。下面咱们来介绍一下如何”指示“以动态生成代理类。
(4)动态生成代理类
动态生成代理类的相关代码以下:get

public class Main { 
 
public static void main(String[] args) { 
 
//建立中介类实例 
 
DynamicProxy inter = new DynamicProxy(new Vendor()); 
 
//加上这句将会产生一个$Proxy0.class文件,这个文件即为动态生成的代理类文件 
 
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); 
 
//获取代理类实例sell 
 
Sell sell = (Sell)(Proxy.newProxyInstance(Sell.class.getClassLoader(), new Class[] {Sell.class}, inter)); 
 
//经过代理类对象调用代理类方法,实际上会转到invoke方法调用 
 
sell.sell(); 
 
sell.ad(); 
 
} 
 
}

在以上代码中,咱们调用Proxy类的newProxyInstance方法来获取一个代理类实例。这个代理类实现了咱们指定的接口而且会把方法调用分发到指定的调用处理器。这个方法的声明以下:

复制代码代码以下:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

方法的三个参数含义分别以下:
loader:定义了代理类的ClassLoder;
interfaces:代理类实现的接口列表
h:调用处理器,也就是咱们上面定义的实现了InvocationHandler接口的类实例
咱们运行一下,看看咱们的动态代理是否能正常工做。我这里运行后的输出为:

 

说明咱们的动态代理确实奏效了。上面咱们已经简单提到过动态代理的原理,这里再简单的总结下:首先经过newProxyInstance方法获取代理类实例,然后咱们即可以经过这个代理类实例调用代理类的方法,对代理类的方法的调用实际上都会调用中介类(调用处理器)的invoke方法,在invoke方法中咱们调用委托类的相应方法,而且能够添加本身的处理逻辑。

相关文章
相关标签/搜索