AOP(Aspect Orient Programming),我们一般称为面向方面(切面)编程,作为面向对象的一种补充,用于处理系统中分布于各个模块的横切关注点,比如事务管理、日志、缓存等等。

  AOP实现的关键在于AOP框架自动创建的AOP代理,AOP代理主要分为静态代理(编译时增强)和动态代理(运行时增强),静态代理的代表为AspectJ;而动态代理则以Spring AOP为代表。

(1)使用AspectJ的编译时增强实现AOP

  所谓的静态代理就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强。

  举个实例的例子来说。首先我们有一个普通的Hello类
  public class Hello {
    public void sayHello() {
      System.out.println("hello");
    }
    public static void main(String[] args) {
      Hello h = new Hello();
      h.sayHello();
    }
  }

  使用AspectJ编写一个Aspect
    public aspect TxAspect {
      void around():call(void Hello.sayHello()){
        System.out.println("开始事务 ...");
        proceed();
        System.out.println("事务结束 ...");
      }
     }

  编译完成之后再运行这个Hello类,可以看到以下输出
    开始事务 ...
    hello
    事务结束 ...

  很显然,AOP已经生效了,那么究竟AspectJ是如何在没有修改Hello类的情况下实现代码增强的?
  查看一下编译后的Hello.class
    public class Hello {
      public Hello() {
      }
      public void sayHello() {
        System.out.println("hello");
      }
      public static void main(String[] args) {
        Hello h = new Hello();
          sayHello_aroundBody1$advice(h, TxAspect.aspectOf(), (AroundClosure)null);
      }
    }

  如此,proceed()方法就是回调执行被代理类中的方法。

(2)使用Spring AOP

  Spring AOP使用的动态代理,动态代理就是说AOP不会去修改字节码,而是在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。需导入两个jar包:cglib.jar、asm.jar。

  Spring AOP中的动态代理主要有两种方式:jdk动态代理 、cglib动态代理。jdk动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。jdk动态代理的核心是InvocationHandler接口 、Proxy类。

  当目标类没有实现接口,那么Spring AOP会选择使用cglib来动态代理目标类。cglib(Code Generation Library)是一个代码生成的类库,可以在运行时动态的生成某个类的子类,cglib是通过继承的方式做的动态代理,所以若某个类被标记为final,那么它就无法使用cglib做动态代理的。

  定义一个接口
  public interface Person {
    String sayHello(String name);
  }

  实现类
  @Component
  public class Chinese implements Person {
    @Override
    public String sayHello(String name) {
      System.out.println("-- sayHello() --");
      return name + " hello, AOP";
    }
  }

  

  实现类

  public class MyInvocationHandler implements InvocationHandler {
     private Object target;
     MyInvocationHandler() {
      super();
    }
    MyInvocationHandler(Object target) {
      super();
      this.target = target;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      // 程序执行前加入逻辑,MethodBeforeAdviceInterceptor
      System.out.println("before-----------------------------");
      // 程序执行
      Object result = method.invoke(target, args);
      // 程序执行后加入逻辑,MethodAfterAdviceInterceptor
      System.out.println("after------------------------------");
      return result;
    }
  }

  测试类 

  public class Test {
  /**
  * JDK动态代理测试类
  */
  public static void main(String[] args) {
    Chinese chinese= new Chinese();
    MyInvocationHandler mih = new MyInvocationHandler(chinese);
    Class<?> cls = chinese.getClass();
    /**
    * loader 类加载器
    * interfaces 实现接口
    * h InvocationHandler
    */
    Person p = (Person)Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), h);
    System.out.println(p.sayHello("张三"));
    }
  }

  输出

  before-----------------------------

  张三 hello, AOP

  after------------------------------

  

  我们再来看看不是用接口的情况,修改Chinese类

  实现类
  @Component
  public class Chinese {
    public String sayHello(String name) {
      System.out.println("-- sayHello() --");
      return name + " hello, AOP";
    }
  }

  

  实现类 

  public class CglibProxy implements MethodInterceptor {
    public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable {
      System.out.println("before-------------");
      // 执行目标类add方法
      proxy.invokeSuper(object, args);
      System.out.println("after--------------");
      return null;
    }
  }

  目标类的工厂Factory类

  public class Factory {
    //获得增强之后的目标类,即添加了切入逻辑advice之后的目标类
    public static Base getInstance(CglibProxy proxy) {
      Enhancer enhancer = new Enhancer();
      enhancer.setSuperclass(Chinese.class);
      //回调方法的参数为代理类对象CglibProxy,最后增强目标类调用的是代理类对象CglibProxy中的intercept方法
      enhancer.setCallback(proxy);
      // 此刻,base不是单纯的目标类,而是增强过的目标类
      Chinese chinese = (Chinese) enhancer.create();
      return chinese;
    }
  }

  测试类  

  public class Test {
    public static void main(String[] args) {
      CglibProxy proxy = new CglibProxy();
      // base为生成的增强过的目标类
      Chinese chinese = Factory.getInstance(proxy);
      System.out.println(chinese.sayHello("张三"));
    }
  }

  输出

  before-----------------------------

  张三 hello, AOP

  after------------------------------

AOP面向切面的实现的更多相关文章

  1. AOP 面向切面编程, Attribute在项目中的应用

    一.AOP(面向切面编程)简介 在我们平时的开发中,我们一般都是面对对象编程,面向对象的特点是继承.多态和封装,我们的业务逻辑代码主要是写在这一个个的类中,但我们在实现业务的同时,难免也到多个重复的操 ...

  2. AOP面向切面编程的四种实现

     一.AOP(面向切面编程)的四种实现分别为最原始的经典AOP.代理工厂bean(ProxyFacteryBean)和默认自动代理DefaultAdvisorAutoProxyCreator以及Bea ...

  3. Javascript aop(面向切面编程)之around(环绕)

    Aop又叫面向切面编程,其中“通知”是切面的具体实现,分为before(前置通知).after(后置通知).around(环绕通知),用过spring的同学肯定对它非常熟悉,而在js中,AOP是一个被 ...

  4. Method Swizzling和AOP(面向切面编程)实践

    Method Swizzling和AOP(面向切面编程)实践 参考: http://www.cocoachina.com/ios/20150120/10959.html 上一篇介绍了 Objectiv ...

  5. [转] AOP面向切面编程

    AOP面向切面编程 AOP(Aspect-Oriented Programming,面向切面的编程),它是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术. ...

  6. C# AOP 面向切面编程之 调用拦截

    有时候我们需要在代码中对方法调用进行拦截,并修改参数和返回值,这种操作叫做AOP(面向切面编程) 不过需要注意的是,AOP的效率很慢,在需要高效率场合慎用. 以下是C#的AOP方法: 首先建立一个控制 ...

  7. 【原创】Android AOP面向切面编程AspectJ

    一.背景: 在项目开发中,对 App 客户端重构后,发现用于统计用户行为的友盟统计代码和用户行为日志记录代码分散在各业务模块中,比如在视频模块,要想实现对用户对监控点的实时预览和远程回放行为进行统计, ...

  8. 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之十 || AOP面向切面编程浅解析:简单日志记录 + 服务切面缓存

    代码已上传Github+Gitee,文末有地址 上回<从壹开始前后端分离[ .NET Core2.0 Api + Vue 2.0 + AOP + 分布式]框架之九 || 依赖注入IoC学习 + ...

  9. 论AOP面向切面编程思想

    原创: eleven 原文:https://mp.weixin.qq.com/s/8klfhCkagOxlF1R0qfZsgg [前言] AOP(Aspect-Oriented Programming ...

  10. 学习笔记: AOP面向切面编程和C#多种实现

    AOP:面向切面编程   编程思想 OOP:一切皆对象,对象交互组成功能,功能叠加组成模块,模块叠加组成系统      类--砖头     系统--房子      类--细胞     系统--人    ...

随机推荐

  1. ltp 测试流程及测试脚本分析

    LTP介绍 (2011-03-25 18:03:53) 转载▼ 标签: ltp linux 压力测试 杂谈 分类: linux测试 LTP介绍 一.LTP介绍1.简介LTP(Linux Test Pr ...

  2. Linux 文件隐藏属性-创建文件默认权限

    Linux特殊权限介绍 # password原本只有root可以访问但是为什么普通用户在修改密码的时候也可以改里面的内容. [root@gong ~]# ll /usr/bin/passwd -rws ...

  3. nosql数据库之Redis概念及基本操作

    一.概述 redis是一种nosql数据库(非关系型数据库),他的数据是保存在内存中,同时redis可以定时把内存数据同步到磁盘,即可以将数据持久化,并且他比memcached支持更多的数据结构(st ...

  4. Java中生成唯一标识符的方法

    有时候业务需要生成唯一标识符,但又不能依赖于数据库中自动递增的字段产生唯一ID,比如多表同一字段需要统一一个唯一ID,此时我们就需要用程序来生成一个唯一的全局ID. UUID UUID是指在一台机器上 ...

  5. Linux - last 命令

    前言 为啥写这篇?因为听 grep.sed 教程的时候有这个命令 栗子 加上工作中,运维给我排查问题的时候也用到了,感觉挺重要,先了解为敬! 命令作用 显示上次登录用户的列表 这个是在 Linux 下 ...

  6. GO语言复合类型02---数组

    package main import "fmt" /* 固定长度.固定类型的数据容器 */ /*数组的声明*/ func main031() { //var array [5]i ...

  7. 情景剧:C/C++中的未定义行为(undefined behavior)

    写在前面 本文尝试以情景剧的方式,轻松.直观地解释C/C++中未定义行为(undefined behavior)的概念.设计动机.优缺点等内容1,希望读者能够通过阅读本文,对undefined beh ...

  8. NVIDIA GPU上的直接线性求解器

    NVIDIA GPU上的直接线性求解器 NVIDIA cuSOLVER库提供了密集且稀疏的直接线性求解器和本征求解器的集合,它们为计算机视觉,CFD,计算化学和线性优化应用程序提供了显着的加速.cuS ...

  9. 实时双频Wi-Fi如何实现下一代车内连接

    实时双频Wi-Fi如何实现下一代车内连接 How real simultaneous dual band Wi-Fi enables next-generation in-vehicle connec ...

  10. 保存数据到csv文件报错:Permission denied: './train_data.csv'

    如果你此前已经输出,创建了文件,很有可能是你打开了此文件,导致写入不进去报错,关掉文件重新运行程序即可!