Java核心库实现简单的AOP

 更新时间:2020年08月19日 11:39:46   作者:弗兰克的猫  
这篇文章主要介绍了如何用Java核心库实现简单的AOP,帮助大家为了更好的理解和学习AOP的思想,感兴趣的朋友可以了解下

  Spring是一个十分火热开源框架,而AOP(面向切面编程)则是Spring最重要的概念之一,为了更好的理解和学习AOP的思想,使用核心库来实现一次不失为一个好方法。

  首先介绍一下AOP的概念,AOP(Aspect Oriented Programming),即面向切面编程,所谓的面向切面编程,就是从一个横切面的角度去设计代码的思想,传统的OOP思想是用封装继承和多态构造一种纵向的层次关系,但不适合定义横向的关系,而AOP思想则对此进行了很好的补充。

  例如日志管理代码往往横向的散布在很多对象层次中,但跟它对应的对象的核心功能可以说是毫无关系,还有很多类似的代码,如权限验证,调试输出,事务处理等,也都是如此,这样的话就不利于代码的复用和管理了。

  这时候AOP技术就应运而生了,它利用“横切”技术,深入封装对象的内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于后续的可操作性和可维护性。

  那么AOP又是如何实现的呢?

  答案是动态代理(关于代理会有另外篇章做详细介绍,这里就不赘述了)。实现动态代理有两种方式,一种是JDK动态代理,一种是CGLib动态代理。

  那么分别使用两种方式来做一个简单的栗子。

  先设计一个场景,假设我们有一个计算接口ICalculator和实现了该接口的计算器类CalculatorImpl。

public interface ICalculator {
  //加法运算
  public int add(int a,int b);
  //减法
  public int subtract(int a,int b);
  //乘法
  public int multiply(int a,int b);
  //除法
  public int devide(int a,int b);
}
public class CalculatorImpl implements ICalculator{
  @Override
  public int add(int a, int b) {
    return a + b;
  }

  @Override
  public int subtract(int a, int b) {
    return a - b;
  }

  @Override
  public int multiply(int a, int b) {
    return a * b;
  }

  @Override
  public int devide(int a, int b) {
    return a / b;
  }
}

  如何在不改动原来计算器类内部代码的情况下记录计算器各个方法使用的总次数呢?

  有了动态代理后,其实就很简单了,先创建一个类并实现InvocationHandler接口,覆盖invoke方法,

public class TestHandler implements InvocationHandler {

  private Object targetObject;
  private int useTimes;

  //绑定委托对象,并返回代理类
  public Object bind(Object targetObject){
    this.targetObject = targetObject;
    return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //do something
    before();
    Object result = method.invoke(targetObject,args);
    after();
    return result;
  }
  private void before(){
    System.out.println("we can do something before calculate.");
  }

  private void after(){
    useTimes++;
    System.out.println("已使用:"+useTimes+"次");
  }
}

  别看代码好像有点多,其实主要的方法就是invoke方法,里面的Object result = method.invoke(targetObject,args);相当于继续用原来的参数执行原来方法。这里的before和after为自定义的函数,可以在目标代码执行前后做一些我们想要做的事情,比如这里的使用次数统计。

  在bind方法里,传入目标代理对象,并返回一个代理类实例。接下来我们看看如何使用:

public class TestProxy {
  public static void main(String[] args) {
    TestHandler proxy = new TestHandler();
    ICalculator calculator = (ICalculator)proxy.bind(new CalculatorImpl());
    int result = calculator.add(1,2);
    System.out.println("result is:"+result);
    result = calculator.subtract(3,2);
    System.out.println("result is:"+result);
    result = calculator.multiply(4,6);
    System.out.println("result is:"+result);
    result = calculator.devide(6,2);
    System.out.println("result is:"+result);
  }
}

  我们先定义一个TestHandler,然后通过bind方法来获得一个代理实例,之后我们就可以直接使用这个实例了。运行结果如下:

we can do something before calculate.
已使用:1次
result is:3
we can do something before calculate.
已使用:2次
result is:1
we can do something before calculate.
已使用:3次
result is:24
we can do something before calculate.
已使用:4次
result is:3

  这样我们就实现了不修改CalculatorImpl内部代码的情况下对代码进行扩展。

  接下来用CGLib的方式来实现一次。

  先创建一个类来实现MethodInterceptor接口,并覆盖intercept方法。其他代码跟使用JDK代理大同小异,仅仅是获取代理对象的过程有所差异。

public class CGLibProxy implements MethodInterceptor {
  private int useTimes;
  private Object target;

  public Object getInstance(Object target){
    this.target=target;
    Enhancer enhancer =new Enhancer();
    enhancer.setSuperclass(this.target.getClass());
    enhancer.setCallback(this);
    return enhancer.create();
  }

  @Override
  public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    before();
    Object result = methodProxy.invokeSuper(o,objects);
    after();
    return result;
  }

  private void before(){
    System.out.println("we can do something before calculate.");
  }

  private void after(){
    useTimes++;
    System.out.println("已使用:"+useTimes+"次");
  }
}

  测试一下:

public class TestCGLibProxy {
  public static void main(String[] args) {
    CGLibProxy cgLibProxy = new CGLibProxy();
    ICalculator calculator = (ICalculator) cgLibProxy.getInstance(new CalculatorImpl());
    int result = calculator.add(1,2);
    System.out.println("result is:"+result);
    result = calculator.subtract(3,2);
    System.out.println("result is:"+result);
    result = calculator.multiply(4,6);
    System.out.println("result is:"+result);
    result = calculator.devide(6,2);
    System.out.println("result is:"+result);
  }
}

  运行结果如下:

we can do something before calculate.
已使用:1次
result is:3
we can do something before calculate.
已使用:2次
result is:1
we can do something before calculate.
已使用:3次
result is:24
we can do something before calculate.
已使用:4次
result is:3

  现在我们得到了同样的结果。(需要导入两个包,cglib-2.2.2.jar asm-3.3.jar)

  两种方法各有所长,JDK代理需要先设置一个接口,然后才能实现代理,这是它的缺点,也是它的优点,缺点是这样会麻烦一点,而且无法对那些已经封装好的,没有实现接口的类进行代理,而CGLib代理的方式不需要使用接口。但也正是因为如此,JDK代理的方式仅仅拦截类中覆盖接口的方法,而CGLib则会拦截类的所有方法调用。两者各有利弊,所以需要具体情况具体分析。在Spring中也是混杂使用了两种代理模式。

以上就是Java核心库实现简单的AOP的详细内容,更多关于Java 实现aop的资料请关注脚本之家其它相关文章!

相关文章

  • 实战分布式医疗挂号通用模块统一返回结果异常日志处理

    实战分布式医疗挂号通用模块统一返回结果异常日志处理

    这篇文章主要为大家介绍了实战分布式医疗挂号系统之统一返回结果统一异常处理,统一日志处理到通用模块示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助
    2022-04-04
  • spring cloud-给Eureka Server加上安全的用户认证详解

    spring cloud-给Eureka Server加上安全的用户认证详解

    这篇文章主要介绍了spring cloud-给Eureka Server加上安全的用户认证详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01
  • 解决java 查看JDK中底层源码的实现方法

    解决java 查看JDK中底层源码的实现方法

    本篇文章是对在java中查看JDK中底层源码的解决方法进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • JAVA实现空间索引编码——GeoHash的示例

    JAVA实现空间索引编码——GeoHash的示例

    本篇文章主要介绍了JAVA实现空间索引编码——GeoHash的示例,如何从众多的位置信息中查找到离自己最近的位置,有兴趣的朋友可以了解一下
    2016-10-10
  • 详解spring中使用solr的代码实现

    详解spring中使用solr的代码实现

    本篇文章主要介绍了详解spring中使用solr的代码实现,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-05-05
  • 5种java排序算法汇总工具类

    5种java排序算法汇总工具类

    这篇文章主要总结了java的快速排序,希尔排序,插入排序,堆排序,归并排序五种排序算法,感兴趣的小伙伴们可以参考一下
    2016-08-08
  • Spring使用@Filter注解创建自定义过滤器

    Spring使用@Filter注解创建自定义过滤器

    Spring 中鲜为人知但非常有用的注解之一是 @Filter,它支持自定义过滤器,下面我们就来深入研究一下如何使用 Spring 的 @Filter 注解来创建自定义过滤器吧
    2023-11-11
  • javaweb实现投票系统

    javaweb实现投票系统

    这篇文章主要为大家详细介绍了javaweb实现投票系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-09-09
  • Java设计模式中的桥接模式

    Java设计模式中的桥接模式

    这篇文章主要介绍了Java设计模式中的桥接模式,其是一种结构型设计模式,是指将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变
    2022-07-07
  • 如何用idea数据库编写快递e站

    如何用idea数据库编写快递e站

    这篇文章主要介绍了如何用idea数据库编写快递e站,本文通过图文实例相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-01-01

最新评论