1. 概述
动态代理 是指在运行时,动态地创建目标类的代理对象,并对其中特定的方法进行拦截或增强的技术。这种技术主要用于在不修改目标类代码的情况下,增强目标类的功能。
在Java中,动态代理主要基于Java的反射机制和接口来实现。当一个类实现了某个接口,并且需要在不修改这个类代码的前提下增加一些新的方法或者对原有方法进行一些处理(比如添加日志、事务管理、权限校验等),就可以使用动态代理来实现。即它可以无侵入式的修改代码。
2. 讲故事
为了方便理解,我这里举一个生活中的故事吧,前不久报名了微软云的认证考试,报名之后前往考点,我才发现这个考点其实根本就不是微软公司亲自设立的,而是有其他公司代理的,因为微软虽大,但是不可能在世界每一个城市都亲自设立办公处或者说是认证考点吧。这种情况下微软就需要请其他公司代理。帮他管理考试过程。那么微软和代理公司在这个过程中分别做了什么呢?
首先是代理公司:
- 代理公司需要提供考点
- 当考生到了考点后,大代理公司的工作人员,需要对参考的考生进行检查确认考生没有夹带或使用作弊工具
- 代理公司需要查询考生证件,确包是考生本人,而不是代考
- 代理公司需要对考生全身照和寸照,用于给微软公司证明考生无夹带,以及将寸照传入电子证书上。
- 代理公司需要提供考试电脑,并帮助考试宣讲考试步骤,考试过程摄像。教考试如何远程获取使用微软考题。
- 代理公司维持考场秩序
- 如果考生考过了,代理公司需要打印微软发送的证书,发给考生。
而微软做了什么呢:
- 管理考生报名
- 网上收取考生考试费用
- 考试时远程提供题库
- 远程发送证书
通过上面我们可以看到,代理给微软节省了很多成本,也省了很多事,而代理公司通过这个过程也可以做一个兼职赚了一笔外快。是一个双赢的局面。生活中这样的例子有很多。
所以我们程序里面的动态代理,可以有很多类似的效果,为我们程序员减少重复代码,提高扩展性等都很有用处。
3.动态代理的实现步骤如下:
- 定义接口:首先定义一个接口,这个接口定义了目标类需要实现的方法。
package com.mycompany.proxyreflection2;public interface ExamServer {String examAz900();String examAz700(); }
- 实现目标类:创建一个类实现上述接口,这个类就是目标类。
package com.mycompany.proxyreflection2;public class TargetCompanyExamServerImpl implements ExamServer {@Overridepublic String examAz900() {return "This is microsoft, wlc exam azure 900, transfer the question bank to proxy";}@Overridepublic String examAz700() {return "This is microsoft, wlc exam azure 700, transfer the question bank to proxy";} }
- 创建InvocationHandler:创建一个实现了InvocationHandler接口的类,这个类将负责拦截并处理对目标类方法的调用。
package com.mycompany.proxyreflection2;import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method;public class ProxyCompanyInvocationHandler implements InvocationHandler {private final Object target; // 目标对象 举例中的微软对象public ProxyCompanyInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 在调用目标方法之前执行一些操作(例如日志记录)System.out.println("代理公司需要提供考点");System.out.println("当考生到了考点后,大代理公司的工作人员,需要对参考的考生进行检查确认考生没有夹带或使用作弊工具");System.out.println("代理公司需要查询考生证件,确包是考生本人,而不是代考");System.out.println("代理公司需要对考生全身照和寸照,用于给微软公司证明考生无夹带,以及、便将寸照传入电子证书上。");System.out.println("代理公司需要提供考试电脑,并帮助考试宣讲考试步骤,考试过程摄像。教考试如何远程获取使用微软考题。");// 调用目标对象上的方法, 获取微软考题Object result = method.invoke(target, args);System.out.println(result);// 在调用目标方法之后执行一些操作(例如日志记录)System.out.println("代理公司维持考场秩序");System.out.println("如果考生考过了,代理公司需要打印微软发送的证书,发给考生。");return result;} }
- 创建代理对象:使用Proxy类的newProxyInstance方法,传入目标类的类加载器、目标类实现的接口列表以及InvocationHandler实例,来创建代理对象。
package com.mycompany.proxyreflection2;import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy;public class ProxyCompany {public static <T> T createProxy(T target) {// 获取目标对象的类加载器和接口类型ClassLoader classLoader = target.getClass().getClassLoader();Class<?>[] interfaces = target.getClass().getInterfaces();// 创建一个 InvocationHandler 实例InvocationHandler handler = new ProxyCompanyInvocationHandler(target);// 使用 Proxy 类的静态方法 newProxyInstance 创建代理对象T proxy = (T) Proxy.newProxyInstance(classLoader, interfaces, handler);return proxy;} }
- 调用方法:通过代理对象调用目标类的方法时,会首先被InvocationHandler的invoke方法拦截,可以在这个方法中添加额外的处理逻辑,然后再调用目标类的方法。
package com.mycompany.proxyreflection2;public class ExamApi {public static void main(String[] args) {// 创建一个 ExamServer 的实现类实例ExamServer myService = new TargetCompanyExamServerImpl();// 使用 ProxyFactory 创建一个 ExamServer 的代理实例ExamServer proxyService = ProxyCompany.createProxy(myService);// 调用代理实例上的方法System.out.println("-----------------Start proxy exam Az 700----------------");proxyService.examAz700();System.out.println("-----------------Start proxy exam Az 900----------------");proxyService.examAz900();} }
运行结果:
-----------------Start proxy exam Az 700----------------
代理公司需要提供考点
当考生到了考点后,大代理公司的工作人员,需要对参考的考生进行检查确认考生没有夹带或使用作弊工具
代理公司需要查询考生证件,确包是考生本人,而不是代考
代理公司需要对考生全身照和寸照,用于给微软公司证明考生无夹带,以及、便将寸照传入电子证书上。
代理公司需要提供考试电脑,并帮助考试宣讲考试步骤,考试过程摄像。教考试如何远程获取使用微软考题。
This is microsoft, wlc exam azure 700, transfer the question bank to proxy
代理公司维持考场秩序
如果考生考过了,代理公司需要打印微软发送的证书,发给考生。
-----------------Start proxy exam Az 900----------------
代理公司需要提供考点
当考生到了考点后,大代理公司的工作人员,需要对参考的考生进行检查确认考生没有夹带或使用作弊工具
代理公司需要查询考生证件,确包是考生本人,而不是代考
代理公司需要对考生全身照和寸照,用于给微软公司证明考生无夹带,以及、便将寸照传入电子证书上。
代理公司需要提供考试电脑,并帮助考试宣讲考试步骤,考试过程摄像。教考试如何远程获取使用微软考题。
This is microsoft, wlc exam azure 900, transfer the question bank to proxy
代理公司维持考场秩序
如果考生考过了,代理公司需要打印微软发送的证书,发给考生。
这种方式的优点是可以在不修改目标类代码的情况下增加新的功能,提高了系统的灵活性和可扩展性。但是,由于Java的动态代理是基于接口的,所以目标类必须实现一个或多个接口,否则无法使用动态代理。