集装箱学习(两):动手模拟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应用到实际的面向对象开发。
版权声明:本文博主原创文章。博客,未经同意不得转载。