Java动态代理
利用了代理模式,一个通俗易懂的说法:给某一个对象提供一个代理,并由代理对象来控制对真实对象的访问。
简单说明
代理模式是Java中常见的设计模式。
其特征是代理类和委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把信息转发给委托类,以及事后处理消息等。
代理类与委托类之间通常会存在关联关系,一个代理类对象与一个委托类对象关联,代理类对象本身并不真正实现服务,而是通过委托类对象的相关方法来提供特定服务。

静态代理
需要代理对象和目标对象实现同样的接口。
**缺点:**当需要的代理的对象过多就需要实现大量的代理类,并且当接口增加方法,目标对象与代理对象都要进行修改
直接用参考文章的一个demo来演示一下什么是静态代理,理解一下
Event.java:
1
2
3
4
5
6
|
//接口类
package java_foundation;
public interface Event {
void sale();
}
|
Text.java
1
2
3
4
5
6
7
8
|
//委托类
package java_foundation;
public class Text implements Event {
public void sale(){
System.out.println("要卖房子了");
}
}
|
MyTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//代理类
package java_foundation;
public class MyTest implements Event{
private Event instances;
public MyTest(Event a){
this.instances = a;
}
public void sale(){
instances.sale();
System.out.println("售价2000元");
}
}
|
最终的测试类:Test.java
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//测试类
package java_foundation;
public class Main{
public static void main(String[] args) throws Exception{
System.out.println("======使用代理前====");
Text a = new Text();
a.sale();
System.out.println("======使用代理后====");
MyTest b = new MyTest(new Text());
b.sale();
}
}
|
只要搞清楚对象的关系,这里的代码就不难理解了。最后结果如下:
1
2
3
4
5
|
======使用代理前====
要卖房子了
======使用代理后====
要卖房子了
售价2000元
|
也就是可以将代理设想为这里卖房子的中介,中介来给你定义具体的内容,你只需要把大体的说明就行了。
我们这里是用的接口类作为接收参数的类型,并且构造函数的接收的参数类型也都是接口类型。
有一个点说明一下,这里为什么这里要使用Event接口类型作为媒介呢,应该是因为委托类实现了Event接口,这样就可以使得相应的Text()对象可以成为这个类型,同时代理类MyTest也接口了Event接口,所以MyTest类也可以接受Event类型的数据。
这里最重要的实现代理的操作就是代理类中的如下代码:
1
2
3
4
|
public void sale(){
instances.sale();
System.out.println("售价2000元");
}
|
就是因为这串代码导致的最终输出为
因为我们new后将instance对应到了委托类的对象,此时就会调用Text.java中的sale()方法,所以其实一般代理类的重要代码都会调用两次。简单流程图如下

动态代理
与静态代理相同,需要公共接口,委托类,代理类。区别就是动态代理是利用反射机制在运行时创建代理类。这里需要用到位于Java.lang.reflect
下的Proxy类
与InvocationHandler接口
。
InvocationHandler接口
:负责提供调用代理的操作
1
2
3
4
|
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
|
其中proxy
为代理类的实例对象,method
表示调用的方法名,args[]
为调用方法的参数数组
这个接口定义了一个invoke()方法,每个代理对象都有一个关联的接口。这个是很重要的:当代理对象上调用任意方法时,该方法会被自动转发到InvocationHandler.invoke()
方法进行调用
位于java.lang.reflect.Proxy
,提供了静态方法用于得到代理对象
1
|
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException{}
|
继续使用前面的那个例子,结合代码,我们这里需要获得类加载器,相对应的方法,直接给代码:
委托类和接口不变,
Text.java:
1
2
3
4
5
6
7
8
|
//委托类
package java_foundation;
public class Text implements Event {
public void sale(){
System.out.println("要卖房子了");
}
}
|
Event.java:
1
2
3
4
5
6
|
//接口类
package java_foundation;
public interface Event {
void sale();
}
|
看其他需要修改的代码:
MyTest.java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
//动态获取代理的代理类
package java_foundation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyTest implements InvocationHandler{
private Object instances;
public MyTest(Object a){
this.instances = a;
}
//重写invoke()方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
System.out.println("售价2000元");
Object result = method.invoke(instances,args);
return result;
}
}
|
在这个代理类重写的invoke()方法中,看到了很熟悉的操作,调用方法,对,就是调用了invoke()方法,结合在前面说明InvocationHandler接口类时,大概久能知道这里的调用逻辑。后面看了结果就知道了。
Main.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
//测试类
package java_foundation;
import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
public class Main{
public static void main(String[] args) throws Exception{
//获取委托类的实例对象
Text text = new Text();
//获取CalssLoader
ClassLoader classLoader = text.getClass().getClassLoader();
//获取所有接口
Class[] interfaces = text.getClass().getInterfaces();
//获取一个调用处理器
InvocationHandler invocatinoHandler = new MyTest(text);
//创建代理对象
Event proxy = (Event)Proxy.newProxyInstance(classLoader,interfaces,invocatinoHandler);
proxy.sale();
}
}
|
成功输出
大概就是这这样,其实理解一下就行了,难度不是很高,重点关注invoke()中的代码。
(参考文章有一道题,后面可以学习一下)
参考文章:
https://xz.aliyun.com/t/9197?time__1311=n4%2BxuDgD9DyDnB7QGQD%2FD0WoQ4D55i%3D31YNGt4D
https://blog.csdn.net/DDDYSz/article/details/109451049
https://tttang.com/archive/1769/
https://www.cnblogs.com/whirly/p/10154887.html