回顾:

Spring 中,除去IoCDI 核心之外,AOP 是其另一个关键技术。前面渐进式地写了3篇文章简单学习了容器、控制反转(IoC)、读配置文件实现依赖注入(DI),其一:引入容器,Service Locator其二:引入IoCDI 其三:结合配置文件完善DI 

为了学习 AOP,先用例子(同样是前面用过的报表生成例子,不同的需求使得代码结构有了变化)来理解一下Java中的动态代理。 

问题描述:

开发一个能够根据本地文件路径、远程文件路径生成HTMLPDF格式的报表的应用。由于不同操作系统下的文件路径有不同的路径分隔符,因此这里存在一个特殊要求:接收到文件路径生成报表之前必须验证该文件路径的合法性 

解决方案:

为了重用我们前面的报表生成的代码(先假设着吧,其实这里类结构已经改变了),在此不能修改原来的代码实现,例如我们不能在原来的 generate() 方法内部增加进行验证的功能实现。此时,用Java 的动态代理(Dynamic Proxy,本质上是反射的应用)可以避开修改原始代码实现来横向增加新功能AOP也是一种与OOP同等的编程范式,不过目前的我只能这样子简单地理解:-D)。 

实现方法:

Java 的动态代理技术中,需要关注的是java.lang.reflect 包里面的InvocationHandler接口(最关键)、Proxy类、Method类。代理类需要实现InvocationHandler接口中的 invoke(),使之能够在调用代理类对象的相关方法时,将这一方法的调用转发给目标类(即被代理类,原始类)。 

依据代码实现,设计类图如下:

 简单代码实现如下:

  1. package AOP.dynamic_proxy; 
  2.  
  3. import java.lang.reflect.InvocationHandler; 
  4. import java.lang.reflect.Method; 
  5. import java.lang.reflect.Proxy; 
  6.  
  7. /** 
  8.  *  报表生成的公共接口 
  9.  */ 
  10. interface ReportCreator { 
  11.     public void getHtmlReport(String path); 
  12.  
  13.     public void getPdfReport(String path); 
  14.  
  15. /** 
  16.  *  用于根据【本地】文件路径生成报表  
  17.  */ 
  18. class LocalReportCreator implements ReportCreator { 
  19.  
  20.     public void getHtmlReport(String path) { 
  21.         System.out.println("根据【本地】文件生成【HTML】格式的报表 ..."); 
  22.     } 
  23.  
  24.     public void getPdfReport(String path) { 
  25.         System.out.println("根据【本地】文件生成【PDF】格式的报表 ..."); 
  26.     } 
  27.  
  28. /** 
  29.  *  用于根据【远程】文件路径生成报表  
  30.  */ 
  31. class RemoteReportCreator implements ReportCreator { 
  32.  
  33.     public void getHtmlReport(String path) { 
  34.         System.out.println("根据【远程】文件生成【HTML】格式的报表 ..."); 
  35.     } 
  36.  
  37.     public void getPdfReport(String path) { 
  38.         System.out.println("根据【远程】文件生成【PDF】格式的报表 ..."); 
  39.     } 

 

  1. /** 
  2.  *  动态代理类,实现 InvocationHandler 接口中的 invoke() 
  3.  */ 
  4. class ReportCreatorProxy implements InvocationHandler { 
  5.  
  6.     private ReportCreator targetCreator; 
  7.  
  8.     public ReportCreatorProxy(ReportCreator targetCreator) { 
  9.         this.targetCreator = targetCreator; 
  10.     } 
  11.  
  12.     /** 
  13.      *  实现invoke()方法,增加额外功能 
  14.      */ 
  15.     public Object invoke(Object proxy, Method method, Object[] args) 
  16.             throws Throwable { 
  17.          
  18.         // 在生成报表之前添加验证功能,参数即 args,这里简化了 
  19.         System.out.println("验证 path 参数是否有效 ..."); 
  20.         Object result = method.invoke(this.targetCreator, args); 
  21.  
  22.         return result; 
  23.     } 
  24.  
  25.     /** 
  26.      *  静态工厂方法,用于获取动态代理实例 
  27.      */ 
  28.     public static Object getReportCreator(ReportCreator target) { 
  29.         Class targetClass = target.getClass();  
  30.         /* 
  31.          * loader:  目标类的类加载器 
  32.          * interfaces:  目标类已实现的接口 
  33.          * handler: 转发方法调用的调用处理类实例,即代理类 
  34.          */ 
  35.         ClassLoader loader = targetClass.getClassLoader(); 
  36.         Class[] interfaces = targetClass.getInterfaces(); 
  37.         ReportCreatorProxy handler = new ReportCreatorProxy(target); 
  38.  
  39.         // 创建并返回动态代理类实例 
  40.         return Proxy.newProxyInstance(loader, interfaces, handler); 
  41.     } 

 

  1. // 测试 
  2. public class DynamicProxy { 
  3.     public static void main(String[] args) { 
  4.          
  5.         // 创建目标类对象,即被代理对象、原始对象 
  6.         ReportCreator target = new LocalReportCreator(); 
  7.         // 获取动态代理对象 
  8.         ReportCreator reportCreator = (ReportCreator) ReportCreatorProxy 
  9.                 .getReportCreator(target); 
  10.         // 利用代理对象提供的服务 
  11.         reportCreator.getPdfReport("E://test//table.txt"); 
  12.     } 

测试运行结果:

验证 path 参数是否有效 ...

根据【本地】文件生成【PDF】格式的报表 ...

上面代码中的验证功能是简化成打印语句了,:-D

 

在前面“回顾”那里我之所以说类结构已经有了相当的变化,是因为我意识到好像建造者模式(Builder)很可能适应于两个例子中的此种变化。

Builder)建造者模式的Java实现

 

小结:

这里认识一下Java中的动态代理(本质是反射技术的应用),初步感受一下模拟AOP 的处理方式,接下来考虑结合配置文件、DI、动态代理等完善一下AOP的实现。:-D

 

    以下文章你可能也会感兴趣:

演进式例解控制反转(IoC)、依赖注入(DI

演进式例解控制反转(IoC)、依赖注入(DI之二

结合配置文件、反射完善控制反转(IoC)、依赖注入(DI

 

装饰模式(Decorator)与动态代理的强强联合 

Dynamic Proxy)动态代理模式的Java实现

Factory Method)工厂方法模式的Java实现

Java RMI 框架的工厂方法模式实现

Mediator)中介者模式的Java实现(加修改)