本章以理论 + 实践来帮助你理解代理模式,JDK动态代理的实现方式。通俗易通,希望给各位一点小小的帮助
阅读本章前需要小伙伴们了解Java面向对象,反射的基本知识,没有学习过的小伙伴先去学习吧~~
先来谈谈什么是代理模式
假设这样一个场景,你的公司是一家软件公司,你是一位软件工程师。客户带着需求去找公司显然不会直接和你谈,而是去找商务谈,此时客户认为商务就代表公司。
让我们用一张图来表示代理模式的含义:
显然客户是通过商务去访问软件工程师的,那么商务(代理对象)存在的意义是什么呢?
商务可以进行谈判,比如项目启动前的商务谈判,软件的价格、交付、进度的时间节点等,或者项目完成后的商务追讨应收账款等。商务也有可能在开发软件之前谈判失败,此时商务就会根据公司规则去结束和客户的合作关系,这些都不用软件工程师处理。因此,代理的作用就是,在真实对象访问之前或者之后加入对应的逻辑,或者根据其他规则决定要不要执行真实对象的方法,显然在这个例子里商务控制了客户和软件工程师的访问
经过上面的论述,我们知道商务和软件工程师是代理和被代理的关系,客户是通过商务访问软件工程师的。此时客户就是调用者,商务就是代理对象,软件工程师就是真实对象。
我们需要在调用者之前产生一个代理对象,而这个代理对象需要和真实对象建立代理关系,所以代理必须分为两个步骤:
- 代理对象和真实对象建立关系
- 实现代理对象的代理逻辑方法
在Java中有多种动态代理技术,如JDK、CGLIB、Javassist、ASM,其中最常用的就是JDK动态代理,这是本章讨论的主要内容。注意:JDK动态代理必须借助接口才能产生代理对象,至于原因,在本章中不作讨论,有兴趣和自行研究。
JDK动态代理具体实现
1. 因为JDK动态代理需要真实对象实现接口,所以在这里定义接口
public interface HelloWorld {
public void sayHello();
}
2.然后提供实现类HelloWorldImpl
public class HelloWorldImpl implements HelloWorld{
@Override
public void sayHello() {
System.out.println("Hello World~~");
}
}
这是最简单的Java接口和实现类的关系,此时可以开始动态代理了。按照我们之前的分析,先要建立起代理对象和真实服务对象的关系,然后实现代理逻辑,所以一共分为两个步骤。
3.创建代理对象,绑定代理对象与真实对象的关系
在JDK动态代理中,要实现代理逻辑类必须去实现java.lang.reflect.InvocationHandler接口,
它里面定义了一个invoke()方法,并提供接口数组用语下挂代理对象。下面为代理类的代码。
public class JdkProxyExample implements InvocationHandler {
//真实对象
private Object target;
/**
* 建立代理对象和真实对象的代理关系方法,并返回代理对象
* @param target 真实对象
* @return 代理对象
*/
public Object bing(Object target){
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
/**
*
* @param proxy 代理对象
*
* @param method 当前调度方法
* @param args 当前方法参数
* @return 代理结果返回
* @throws Throwable 异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("进入代理逻辑方法");
System.out.println("在调度真实对象之前的服务");
Object obj = method.invoke(target, args);//相当于调用sayHello方法
System.out.println("在调度真实对象后的方法");
return obj;
}
第一步建立代理与被代理对象的关系,这里使用bind方法去完成的,方法里面首先用类的属性target保存了真实对象,然后通过如下代码建立并生成代理对象
Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
其中newInstance包含了三个参数。
- 第一个是类加载器,我们采用了target本身的类加载器
- 第二个是把生成的动态代理对象下挂在哪些接口下,这个写法就是放在target实现的接口下。HelloWorldImpl对象的接口显然就是HelloWorld,代理对象可以这样声明;HelloWorld proxy = xxxx;。
- 第三个是定义实现方法逻辑的代理类,this表示当前对象,它必须实现InvocationHandler接口的Invoke方法,它就是代理逻辑方法的现实方法
第二步,实现代理逻辑方法。Invoke方法可以实现代理逻辑,Invoke方法的三个参数含义如下:
- proxy 代理对象,就是bind方法生成的对象
- method 当前调度的方法
- args 调度方法的参数
当我们使用代理对象调度方法后,它就会进入到invoke方法里
Object obj = method.invoke(target, args);
这行代码相当于调度真实对象的方法,只是通过反射实现而已。
类比前面的例子,proxy相当于商务对象,target相当于软件工程师对象,bing方法就是建立商务和软件工程师代理关系的方法。而invoke就是商务逻辑,它将控制软件工程师的访问。
测试JDK动态代理
@Test
public void testJdkProxy(){
JdkProxyExample jdkProxyExample = new JdkProxyExample();
//绑定关系,因为挂在HelloWorld接口下,所以声明代理对象 HelloWorld proxy
HelloWorld proxy = (HelloWorld) jdkProxyExample.bing(new HelloWorldImpl());
//注意 此时HelloWorld对象已经是一个代理对象,它会进入代理的逻辑方法invoke
proxy.sayHello();
}
-------------------------------------------------------------------------------------------
结果:
进入代理逻辑方法
在调度真实对象之前的服务
Hello World~~
在调度真实对象后的方法
首先通过bind方法绑定关系,然后在代理对象调度sayHello方法执行了代理的逻辑。
这就是JDK动态代理,这是一种最常用的动态代理,十分重要。代理模式要掌握不容易,小伙伴们可以通过打断点,一步步验证执行 行的步骤,就一定能掌握好它~