JDK动态代理原理浅析(大白话)
之前写过一篇文章来介绍JDK动态代理和静态代理的用法,如果忘记了基本使用的可以去快速回忆一下—— 5分钟搞懂Java静态代理与动态代理。本篇文章主要介绍JDK动态代理的原理
一、什么是动态?
要想回答这个问题,关键在于理解动态这两个字的含义,在理解这个之前,需要知道最基本的静态的含义。
-
首先我们来看第一个小问题——什么是静态?
静态,从名字上理解,顾名思义,是静止不变的状态。比如Java中的static修饰的静态变量,是不能改变的,比如我们写完代码后编译的class文件,也是不能随便改变的,并且他们还有一个共同点:**一旦创建,就不能随便修改了,会一直保持着这种状态,如果你想改变或者加入新的东西,每次都需要手动的再去操作一遍。**这也是静态代理的缺点:如果我想去代理新的接口,那么我就不得不重新编写代码,并且这再次编写的代码在结构上大部分还是和之前的代码是重复的。要说优点的话,可能就是操作简单、易理解了。
-
再来回答第二个小问题——什么是动态?
动态,这其实挺好理解的,说白了就是状态并不是一成不变的,会根据情况发生相应的改变,比如算法中的动态规划,后面的状态都会受到前面状态的影响,这种思想有点像是我们口中常说的“看情况”。说到这里,我们回头对比一下前面的静态,就会发现这有巨大的好处——灵活不死板,做什么之前先看看情况,而不是一开始就想好了以后怎么做。
二、什么是动态代理?
在理解了第一个大问题——什么是动态?之后我们接下来就来说一下什么是动态代理,其实说白了动态代理就是动态生成的代理对象,如果你觉得这个很难理解,我们先来看一下下面我们自己手写的静态代理的样子:
静态代理对象:
//静态代理对象(目标对象的增强)
public class SubjectProxy implements Subject{
//实际的目标对象
private Subject target;
//构造方法
public SubjectProxy(Subject target){
this.subject=subject;
}
//增强后的方法
public void request() {
//前置处理
System.out.println("PreProcess");
//实际的目标对象的方法
target.request();
//后置处理
System.out.println("PostProcess");
}
}
其实动态生成的代理对象和我们自己手写的静态代理”非常类似“,只不过是JDK动态的生成的
我们先看看我们平时是如何使用动态代理的:
1、目标对象所实现的接口
public interface Subject {
void request();
}
2、目标对象
public class SubjectImpl implements Subject{
@Override
public void request() {
System.out.println("目标对象处理请求!");
}
}
3、代理处理器对象(实现InvocationHandler接口)
public class ProxyHandler implements InvocationHandler {
//实际的目标对象
private Subject subject;//持有真实的目标对象的句柄
public ProxyHandler(Subject subject) {
this.subject = subject;
}
//增强后的方法逻辑
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//前置处理
System.out.println("PreProcess");
Object result = method.invoke(subject,args);
//后置处理
System.out.println("PostProcess");
return result;
}
}
4、测试类
public class Demo {
public static void main(String[] args) {
// 目标接口
Subject subject = new Subject();
// 代理处理器
ProxyHandler handler = new ProxyHandler(subject);
// 生成动态代理对象
Object proxy = Proxy.newProxyInstance(loader,
new Class[]{Cook.class, Driver.class},
handler);
// 调用动态代理对象的方法
proxy.request();
}
}
如果你去自己执行了上面的代码,会发现他们的结果都是一样的。在代码层面中 ,也就是是**静态代理类中的request()方法与代理处理器实现的invoke()**方法是一样的,结合这个结论,我们来看看动态代理大概长什么样:
//动态代理对象(目标对象的增强)
public class &Proxy0 extend Proxy implements Subject{
// 目标处理器(其实是在父类Proxy中,这里为了方便理解,拿到子类中)
InvocationHandler invocationHandler;
// 动态代理的构造方法
public &Proxy0(InvocationHandler invocationHandler) {
this.invocationHandler = invocationHandler;
}
//增强后的方法
public void request() {
invocationHandler.invoke();
}
}
为了方便你对比,我把上面静态代理的代码拿下来进行对比:
//静态代理对象(目标对象的增强)
public class SubjectProxy implements Subject{
//实际的目标对象
private Subject target;
//构造方法
public SubjectProxy(Subject target){
this.subject=subject;
}
//增强后的方法
public void request() {
//前置处理
System.out.println("PreProcess");
//实际的目标对象的方法
target.request();
//后置处理
System.out.println("PostProcess");
}
}
三、总结
稍微对比一下就会发现,其实差别并没有特别大,最大的区别就是——动态代理在静态代理的基础之上,将静态代理类中增强后的方法的实现逻辑抽取出来,叫做InvocationHandler接口,通过面向接口的方式让我们程序猿可以自己在外面自己书写增强后的逻辑,并且动态代理对象也和静态代理对象一样都实现了目标对象的接口,因此我们在面向接口编程的时,对代码的影响是非常的小的。
以上 是一些基本原理浅析,之后还会有内容进行补充,如果有问题,欢迎留言讨论