回顾:
在 Spring 中,除去IoC、DI 核心之外,AOP 是其另一个关键技术。前面渐进式地写了3篇文章简单学习了容器、控制反转(IoC)、读配置文件实现依赖注入(DI),其一:引入容器,Service Locator、其二:引入IoC,DI 、其三:结合配置文件完善DI。
为了学习 AOP,先用例子(同样是前面用过的报表生成例子,不同的需求使得代码结构有了变化)来理解一下Java中的动态代理。
问题描述:
开发一个能够根据本地文件路径、远程文件路径生成HTML、PDF格式的报表的应用。由于不同操作系统下的文件路径有不同的路径分隔符,因此这里存在一个特殊要求:接收到文件路径生成报表之前必须验证该文件路径的合法性。
解决方案:
为了重用我们前面的报表生成的代码(先假设着吧,其实这里类结构已经改变了),在此不能修改原来的代码实现,例如我们不能在原来的 generate() 方法内部增加进行验证的功能实现。此时,用Java 的动态代理(Dynamic Proxy,本质上是反射的应用)可以避开修改原始代码实现来横向增加新功能(AOP也是一种与OOP同等的编程范式,不过目前的我只能这样子简单地理解:-D)。
实现方法:
在 Java 的动态代理技术中,需要关注的是java.lang.reflect 包里面的InvocationHandler接口(最关键)、Proxy类、Method类。代理类需要实现InvocationHandler接口中的 invoke(),使之能够在调用代理类对象的相关方法时,将这一方法的调用转发给目标类(即被代理类,原始类)。
依据代码实现,设计类图如下:
- package AOP.dynamic_proxy;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- /**
- * 报表生成的公共接口
- */
- interface ReportCreator {
- public void getHtmlReport(String path);
- public void getPdfReport(String path);
- }
- /**
- * 用于根据【本地】文件路径生成报表
- */
- class LocalReportCreator implements ReportCreator {
- public void getHtmlReport(String path) {
- System.out.println("根据【本地】文件生成【HTML】格式的报表 ...");
- }
- public void getPdfReport(String path) {
- System.out.println("根据【本地】文件生成【PDF】格式的报表 ...");
- }
- }
- /**
- * 用于根据【远程】文件路径生成报表
- */
- class RemoteReportCreator implements ReportCreator {
- public void getHtmlReport(String path) {
- System.out.println("根据【远程】文件生成【HTML】格式的报表 ...");
- }
- public void getPdfReport(String path) {
- System.out.println("根据【远程】文件生成【PDF】格式的报表 ...");
- }
- }
- /**
- * 动态代理类,实现 InvocationHandler 接口中的 invoke()
- */
- class ReportCreatorProxy implements InvocationHandler {
- private ReportCreator targetCreator;
- public ReportCreatorProxy(ReportCreator targetCreator) {
- this.targetCreator = targetCreator;
- }
- /**
- * 实现invoke()方法,增加额外功能
- */
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- // 在生成报表之前添加验证功能,参数即 args,这里简化了
- System.out.println("验证 path 参数是否有效 ...");
- Object result = method.invoke(this.targetCreator, args);
- return result;
- }
- /**
- * 静态工厂方法,用于获取动态代理实例
- */
- public static Object getReportCreator(ReportCreator target) {
- Class targetClass = target.getClass();
- /*
- * loader: 目标类的类加载器
- * interfaces: 目标类已实现的接口
- * handler: 转发方法调用的调用处理类实例,即代理类
- */
- ClassLoader loader = targetClass.getClassLoader();
- Class[] interfaces = targetClass.getInterfaces();
- ReportCreatorProxy handler = new ReportCreatorProxy(target);
- // 创建并返回动态代理类实例
- return Proxy.newProxyInstance(loader, interfaces, handler);
- }
- }
- // 测试
- public class DynamicProxy {
- public static void main(String[] args) {
- // 创建目标类对象,即被代理对象、原始对象
- ReportCreator target = new LocalReportCreator();
- // 获取动态代理对象
- ReportCreator reportCreator = (ReportCreator) ReportCreatorProxy
- .getReportCreator(target);
- // 利用代理对象提供的服务
- reportCreator.getPdfReport("E://test//table.txt");
- }
- }
测试运行结果:
验证 path 参数是否有效 ... 根据【本地】文件生成【PDF】格式的报表 ... |
上面代码中的验证功能是简化成打印语句了,:-D
在前面“回顾”那里我之所以说类结构已经有了相当的变化,是因为我意识到好像建造者模式(Builder)很可能适应于两个例子中的此种变化。
小结:
这里认识一下Java中的动态代理(本质是反射技术的应用),初步感受一下模拟AOP 的处理方式,接下来考虑结合配置文件、DI、动态代理等完善一下AOP的实现。:-D
★ 以下文章你可能也会感兴趣: