当前位置:主页 > java教程 > Java库实现AOP方法

Java核心库实现AOP方法详解

发布:2019-06-21 16:24:52 172


给网友朋友们带来一篇相关的编程文章,网友金访枫根据主题投稿了本篇教程内容,涉及到Java、核心库、AOP、Java库实现AOP方法相关内容,已被689网友关注,相关难点技巧可以阅读下方的电子资料。

Java库实现AOP方法

这篇文章是关于Java的一个疑难杂症,通过利用Java核心库实现简单的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超详细教你写一个学籍管理系统案例

    Java超详细教你写一个学籍管理系统案例

    发布:2022-07-06

    给网友朋友们带来一篇关于Java的教程,这篇文章主要介绍了怎么用Java来写一个学籍管理系统,学籍管理主要涉及到学生信息的增删查改,本篇将详细的实现,感兴趣的朋友跟随文章往下看看吧


  • Java字典生成算法知识点总结

    发布:2019-06-17

    今天小编就为大家分享一篇关于Java字典生成算法讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧


  • Java实现全图背景水印的示例详解

    发布:2023-04-08

    这篇文章主要为大家详细介绍了如何利用Java实现全图背景水印的方法,文中的示例代码讲解详细,具有一定的借鉴价值,需要的可以参考一下


  • 通过Java实现添加或删除PDF中的附件

    发布:2023-04-19

    当我们在制作PDF文件或者PPT演示文稿的时候,为了让自己的文件更全面详细,就会在文件中添加附件。本文为大家整理了Java实现添加或删除PDF中的附件的方法,需要的可以参考下


  • Java GUI实现学生成绩管理系统

    Java GUI实现学生成绩管理系统

    发布:2023-01-09

    给大家整理了关于Java的教程,这篇文章主要为大家详细介绍了Java GUI实现学生成绩管理系统,具有一定的参考价值,感兴趣的小伙伴们可以参考一下


  • 微信公众号服务号推送模板消息设置方法(后端java)

    发布:2023-03-26

    公众号时经常会需要写推送消息,从网上找了一大堆,都不是很全,所以这篇文章主要介绍了微信公众号服务号推送模板消息设置方法的相关资料,需要的朋友可以参考下


  • java中处理json各种各样的转换方法(推荐)

    发布:2023-01-11

    给网友朋友们带来一篇关于java的教程,下面小编就为大家分享一篇java中处理json各种各样的转换方法小结,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧


  • Java中JWT的使用的详细教程

    发布:2023-04-08

    JWT的本质就是一个字符串,它是将用户信息保存到一个Json字符串中,然后进行编码后得到一个JWT token,并且这个JWT token带有签名信息,接收后可以校验是否被篡改,所以可以用于在各方之间安全地将信息作为Json对象传输,本文介绍了Java中JWT的使用,需要的朋友可以参考下


网友讨论