[原]容器学习(二):动手模拟AOP
简单来说,Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器框架。上文已经介绍模拟IoC实现,这篇文章来动手模拟AOP。
AOP简述
面向对象强调"一切皆是对象",是对真实世界的模拟。然而面向对象也并非完美无缺的,它更注重于对象层次结构方面的东西,对于如何更好的管理对象行为内部结构,还存在着些许不足。那么我们如何使这个问题的得到更完美的解决呢?答案就是AOP。
AOP:Aspect-Oriented Programming。AOP是OOP的补充,是GOF的延续。我们知道设计模式是对于面向对象设计中经验的总结,它孜孜不断追求的就是调用者与被调用者之间的解耦。有了设计模式我们可以更有效的利用面向对象的特性,使得整个软件设计更加灵活、优雅。但是设计模式是基于面向对象的思想而形成的,更多的时候关注的是对象层次的东西,在解决对象行为内部问题方面却有些不足。AOP的出现恰恰就是对面向对象思想做出了完美的补充。
说到AOP,我们就不得不来提一下软件的纵向和横向问题。从纵向结构来看就是我们软件系统的各个模块,它主要负责处理我们的核心业务(例如商品订购、购物车查看);而从横向结构来看,我们几乎每个系统又包含一些公共模块(例如权限、日志模块等)。这些公共模块分布于我们各个核心业务之中(例如订购和查看商品明细的过程都需要检查用户权限、记录系统日志等)。这样一来不仅在开发过程中要处处关注公共模块的处理而且开发后维护起来也是十分麻烦。而有了AOP之后将应用程序中的商业逻辑同对其提供支持的通用服务进行分离,使得开发人员可以更多的关注核心业务开发。
利用动态代理实现AOP
下面我们就以一个简单的例子来看一下AOP吧!
点击下载源码:下载
比如说,我们现在要开发的一个应用里面有很多的业务方法,但是,我们现在要对这个方法的执行做全面监控,或部分监控.也许我们就会在要一些方法执行前进行日志记录,我们写个例子看看我们最简单的解决方案
我们先写一个接口UserManager.java代码如下:
publicinterface UserManager { publicvoid add(String userId, String userName); }
add方法里面只是将简单的字符串作为参数,而不是实体,勿怪,讲解的重点不是这里,我们去写个类实现UserManager接口
publicclass UserManagerImp implements UserManager { @Override publicvoid add(String userId, String userName) { System.out.println("成功执行:UserManagerImpl.add()userId-->>" + userId); } }
现在我们要为这个业务方法加上日志记录的业务,我们在不改变原代码的情况下,我们会去怎么做呢?也许,你会去写一个类去实现UserManager接口,并依赖UserManagerImp这个类.代码如下:
public class UserManagerProxyimplements UserManager{ private UserManager userManager; public UserManagerProxy(UserManager userManager){ this.userManager= userManager; } publicvoid addUser(String userId, String userName) { System.out.println("start-->>addUser()userId-->>" + userId); try{ userManager.add(userId,userName); System.out.println("success-->>addUser()"); }catch(Exceptione) { e.printStackTrace(); System.out.println("error-->>addUser()"); thrownew RuntimeException(); } } }
从上面的代码我们可以看出,UserManager 对象是被UserManagerProxy这个所谓的代理所创建的.这样,如果我们以后要把日志记录的功能去掉.那我们只要把得到userManager对象的的具体实现改为UserManager的就可以。上面的代码 就是对AOP的最简单的实现,但是我们接下来想,如果我们要在很多业务逻辑之前加日志的话,那么,我们是不是要去写很多个UserManagerProxy这样的类呢.其实也是一种很麻烦的事.在jdk1.3以后.jdk跟我们提供了一个API java.lang.reflect.InvocationHandler的类. 这个类可以让我们在JVM调用某个类的方法时动态的为些方法做些什么事.让我们把以上的代码改一下来看看效果.
我们一样的去写一个代理类.只不过.让这个类去实现java.lang.reflect.InvocationHandler接口,代码如下:
packagecom.java.drp.pattern; importjava.lang.reflect.InvocationHandler; importjava.lang.reflect.Method; importjava.lang.reflect.Proxy; publicclass LogHandler implements InvocationHandler { /** * 要处理的对象(也就是我们要在方法前后加上业务逻辑的对象) */ privateObject targetObject; /** * 动态生成方法被处理过后的对象 (写法固定) * @param targetObject * @return */ publicObject newProxyInstance(Object targetObject){ this.targetObject= targetObject; returnProxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(), this); } /** *要处理的对象中的每个方法会被此方法送去JVM调用,也就是说,要处理的对象的方法只能通过此方法调用 * 此方法是动态的,不是手动调用的 */ @Override publicObject invoke(Object proxy, Method method, Object[] args) throwsThrowable { System.out.println("将要执行的方法:"+ method.getName()); System.out.print("将要执行的方法中的参数:"); for(int i = 0; i < args.length; i++) { System.out.print(args[i]+ " "); } System.out.println(); Objectresult = null; try{ //执行原来的方法之前记录日志 System.out.println(method.getName()+ "Method Start!"); //JVM通过这条语句执行原来的方法(反射机制) result= method.invoke(targetObject, args); //执行原来的方法之后记录日志 System.out.println(method.getName()+ "Method End!"); }catch (Exception e) { e.printStackTrace(); throwe; } //返回方法返回值给调用者 returnresult; } }
该段代码的执行流程
运行结果:
将要执行的方法:add
将要执行的方法中的参数:001tch
addMethod Start!
添加用户成功
addMethod End!
从上面的例子我们看出,你的任何对象的方法执行之前要加上记录日志的操作都是可以的.LogHandler自动去代理执行被代理对象UserManagerImp中的每一个方法,通过java.lang.reflect.InvocationHandler接口就把我们的代理对象和被代理对象解藕。
总结
上面简单的介绍了如何用动态代理的方式实现AOP,主要是帮助大家理解动态代理AOP的大致思路,以便更好的使用AOP工具,并且把AOP应用到实际的面向对象开发中。