spring AOP是什么?你都拿它做什么?

时间:2017-12-07 10:39

对于最近博主最近写博客的兴致大发,我也在思考:为什么而写博客?在互联网时代,无论你是牛人大咖,还是小白菜鸟,都有发表自己看法的权利。无论你是对的还是错的,都会在这个平台上找到答案。所以,我会尽可能去写自己感兴趣的内容,无论正面或者负面的消息,都尽可能回复我的每一位读者。即使自己只有一个读者,也会坚持写下去。有一个平台,去表达自己,记录自己的点滴,难道不是一种快乐吗?同样,了解技术是一个深入和拓展的过程,需要一个人清晰严谨的逻辑思维。有时候,写博客更像是给自己做笔记,巩固分散的知识!

上一篇文章中,我对spring源码进行了分析讨论,此处不再赘述,有兴趣的同学可以看看向spring大佬低头--大量源码流出解析,本文是对上一篇文章的一个补充。回到正题,为什么会有面向切面编程(AOP)?我们知道java是一个面向对象(OOP)的语言,但它有一些弊端,比如当我们需要为多个不具有继承关系的对象引入一个公共行为,例如日志,权限验证,事务等功能时,只能在在每个对象里引用公共行为,这样做不便于维护,而且有大量重复代码。AOP的出现弥补了OOP的这点不足。

为了阐述清楚spring AOP,我们从将以下方面进行讨论:

1.代理模式。

2.静态代理原理及实践。

3.动态代理原理及实践。

4.spring AOP原理及实战。

1.代理模式。

代理模式:为其他对象提供一种代理以控制对这个对象的访问。这段话比较官方,但我更倾向于用自己的语言理解:比如A对象要做一件事情,在没有代理前,自己来做,在对A代理后,由A的代理类B来做。代理其实是在原实例前后加了一层处理,这也是AOP的初级轮廓。

2.静态代理原理及实践。

静态代理模式:静态代理说白了就是在程序运行前就已经存在代理类的字节码文件,代理类和原始类的关系在运行前就已经确定。废话不多说,我们看一下代码,为了方便阅读,博主把单独的class文件合并到接口中,读者可以直接复制代码运行:

package test.staticProxy; // 接口 public interface IUserDao {     void save();     void find(); } //目标对象 class UserDao implements IUserDao{     @Override     public void save() {         System.out.println("模拟:保存用户!");     }     @Override     public void find() {         System.out.println("模拟:查询用户");     } } /**     静态代理           特点:     1. 目标对象必须要实现接口     2. 代理对象,要实现与目标对象一样的接口  */ class UserDaoProxy implements IUserDao{     // 代理对象,需要维护一个目标对象     private IUserDao target = new UserDao();     @Override     public void save() {         System.out.println("代理操作: 开启事务...");         target.save();   // 执行目标对象的方法         System.out.println("代理操作:提交事务...");     }     @Override     public void find() {         target.find();     } } 

测试结果:                             

测试结果

静态代理虽然保证了业务类只需关注逻辑本身,代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理。再者,如果增加一个方法,除了实现类需要实现这个方法外,所有的代理类也要实现此方法。增加了代码的维护成本。那么要如何解决呢?答案是使用动态代理。

3.动态代理原理及实践。

动态代理模式:动态代理类的源码是在程序运行期间通过JVM反射等机制动态生成,代理类和委托类的关系是运行时才确定的。实例如下:

package test.dynamicProxy;  import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; // 接口 public interface IUserDao {     void save();     void find(); } //目标对象  class UserDao implements IUserDao{     @Override     public void save() {         System.out.println("模拟: 保存用户!");     }     @Override     public void find() {         System.out.println("查询");     } } /**  * 动态代理:  *    代理工厂,给多个目标对象生成代理对象!  *  */ class ProxyFactory {     // 接收一个目标对象     private Object target;     public ProxyFactory(Object target) {         this.target = target;     }     // 返回对目标对象(target)代理后的对象(proxy)     public Object getProxyInstance() {         Object proxy = Proxy.newProxyInstance(             target.getClass().getClassLoader(),  // 目标对象使用的类加载器             target.getClass().getInterfaces(),   // 目标对象实现的所有接口             new InvocationHandler() {           // 执行代理对象方法时候触发                 @Override                 public Object invoke(Object proxy, Method method, Object[] args)                         throws Throwable {                                          // 获取当前执行的方法的方法名                     String methodName = method.getName();                     // 方法返回值                     Object result = null;                     if ("find".equals(methodName)) {                         // 直接调用目标对象方法                         result = method.invoke(target, args);                     } else {                         System.out.println("开启事务...");                         // 执行目标对象方法                         result = method.invoke(target, args);                         System.out.println("提交事务...");                     }                     return result;                 }             }         );         return proxy;     } }   

测试结果如下:                      

测试结果

在运行测试类中创建测试类对象代码中:

IUserDao proxy = (IUserDao)new ProxyFactory(target).getProxyInstance(); 

其实是jdk动态生成了一个类去实现接口,隐藏了这个过程:

class $jdkProxy implements IUserDao{}