欢迎来到宁波培训网 时间
 位置: 宁波上元教育培训网 >> 机械电子 >> 应用案例 >> 正文
  • 机械电子学习
  • -网上报名-
  • 更多

我们为什么要使用AOP?

文章来源:宁波上元教育培训网    作者:Admin    点击次数:     更新时间:2018/4/28

原始代码的写法


既然要通过代码来演示,那必须要有例子,这里我的例子为:


有一个接口Dao有insert、delete、update三个方法,在insert与update被调用的前后,打印调用前的毫秒数与调用后的毫秒数。


首先定义一个Dao接口:

/**
* @author 五月的仓颉
http://www.cnblogs.com/xrq730/p/7003082.html
*/

public interface Dao {

   public void insert();
   
   public void delete();
   
   public void update();
   
}


然后定义一个实现类DaoImpl:

/**
* @author 五月的仓颉
http://www.cnblogs.com/xrq730/p/7003082.html
*/

public class DaoImpl implements Dao {

   @Override
   public void insert() {
       System.out.println("DaoImpl.insert()");
   }

   @Override
   public void delete() {
       System.out.println("DaoImpl.delete()");
   }

   @Override
   public void update() {
       System.out.println("DaoImpl.update()");
   }
   
}


最原始的写法,我要在调用insert()与update()方法前后分别打印时间,就只能定义一个新的类包一层,在调用insert()方法与update()方法前后分别处理一下,新的类我命名为ServiceImpl,其实现为:

/**
* @author 五月的仓颉
http://www.cnblogs.com/xrq730/p/7003082.html
*/

public class ServiceImpl {

   private Dao dao = new DaoImpl();
   
   public void insert() {
       System.out.println("insert()方法开始时间:" + 
                  System.currentTimeMillis());
       dao.insert();
       System.out.println("insert()方法结束时间:" + 
       System.currentTimeMillis());
   }
   
   public void delete() {
       dao.delete();
   }
   
   public void update() {
       System.out.println("update()方法开始时间:" + 
       System.currentTimeMillis());
       dao.update();
       System.out.println("update()方法结束时间:" + 
       System.currentTimeMillis());
   }
   
}


这是最原始的写法,这种写法的缺点也是一目了然:

  1. 方法调用前后输出时间的逻辑无法复用,如果有别的地方要增加这段逻辑就得再写一遍

  2. 如果Dao有其它实现类,那么必须新增一个类去包装该实现类,这将导致类数量不断膨胀


使用装饰器模式


接着我们使用上设计模式,先用装饰器模式,看看能解决多少问题。装饰器模式的核心就是实现Dao接口并持有Dao接口的引用,我将新增的类命名为LogDao,其实现为:

/**
* @author 五月的仓颉
http://www.cnblogs.com/xrq730/p/7003082.html
*/

public class LogDao implements Dao {

   private Dao dao;
   
   public LogDao(Dao dao) {
       this.dao = dao;
   }

   @Override
   public void insert() {
       System.out.println("insert()方法开始时间:" +
        System.currentTimeMillis());
       dao.insert();
       System.out.println("insert()方法结束时间:" + 
       System.currentTimeMillis());
   }

   @Override
   public void delete() {
       dao.delete();
   }

   @Override
   public void update() {
       System.out.println("update()方法开始时间:" + 
       System.currentTimeMillis());
       dao.update();
       System.out.println("update()方法结束时间:" + 
       System.currentTimeMillis());
   }

}


在使用的时候,可以使用"Dao dao = new LogDao(new DaoImpl())"的方式,这种方式的优点为:

  • 透明,对调用方来说,它只知道Dao,而不知道加上了日志功能

  • 类不会无限膨胀,如果Dao的其它实现类需要输出日志,只需要向LogDao的构造函数中传入不同的Dao实现类即可。


不过这种方式同样有明显的缺点,缺点为:

  • 输出日志的逻辑还是无法复用。

  • 输出日志的逻辑与代码有耦合,如果我要对delete()方法前后同样输出时间,需要修改LogDao。

但是,这种做法相比最原始的代码写法,已经有了很大的改进。


使用代理模式


接着我们使用代理模式尝试去实现最原始的功能,使用代理模式,那么我们就要定义一个InvocationHandler,我将它命名为LogInvocationHandler,其实现为:

/**
* @author 五月的仓颉
http://www.cnblogs.com/xrq730/p/7003082.html
*/

public class LogInvocationHandler implements InvocationHandler {

   private Object obj;
   
   public LogInvocationHandler(Object obj) {
       this.obj = obj;
   }
   
   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       String methodName = method.getName();
       if ("insert".equals(methodName) || "update".equals(methodName)) {
           System.out.println(methodName + "()方法开始时间:" + 
           System.currentTimeMillis());
           Object result = method.invoke(obj, args);
           System.out.println(methodName + "()方法结束时间:" + 
           System.currentTimeMillis());
           
           return result;
       }
       
       return method.invoke(obj, args);
   }
   
}


其调用方式很简单,我写一个main函数:

/**
* @author 五月的仓颉
http://www.cnblogs.com/xrq730/p/7003082.html
*/

public static void main(String[] args) {
   Dao dao = new DaoImpl();
       
   Dao proxyDao = (Dao)Proxy.newProxyInstance(
   LogInvocationHandler.class.getClassLoader(), 
   new Class<?>[]{Dao.class}, new LogInvocationHandler(dao));
       
   proxyDao.insert();
   System.out.println("----------分割线----------");
   proxyDao.delete();
   System.out.println("----------分割线----------");
   proxyDao.update();
}


结果就不演示了,这种方式的优点为:

  • 输出日志的逻辑被复用起来,如果要针对其他接口用上输出日志的逻辑,只要在newProxyInstance的时候的第二个参数增加Class数组中的内容即可。

这种方式的缺点为:

  • JDK提供的动态代理只能针对接口做代理,不能针对类做代理。

  • 代码依然有耦合,如果要对delete方法调用前后打印时间,得在LogInvocationHandler中增加delete方法的判断。

上一篇: 西门子直流伺服驱动系统故障维修10例 下一篇: 没有了
网上报名>>
姓名:  性别:
电话: 
地址:
课程:

联系地址:宁波市海曙区中山东路137号7楼
咨询热线:0574-87325693、87325823、87326973、0574-87329343、87329353、87329683、87327805、87323725、87324192

Copyright © 无锡市上元教育咨询有限公司 All Rights Reserved   苏ICP备09027265号-70

邦元教育工作时间