AspectJ-AOP和Spring-AOP

AspectJ-AOP和Spring-AOP

AspectJ-AOP

简单介绍

AspectJ是一个java实现的AOP框架。

  • 它能够对java代码进行AOP编译(一般在编译期进行)
  • 让java代码具有AspectJ的AOP功能(当然需要特殊的编译器)
  • 可以这样说AspectJ是目前实现AOP框架中最成熟,功能最丰富的语言,更幸运的是

举例:

这里先进行一个简单案例的演示,然后引出AOP中一些晦涩难懂的抽象概念,放心,通过本篇博客,我们将会非常轻松地理解并掌握它们。编写一个HelloWord的类,然后利用AspectJ技术切入该类的执行过程。

/**
 * Created by zejian on 2017/2/15.
 * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创]
 */
public class HelloWord {

    public void sayHello(){
        System.out.println("hello world !");
    }
    public static void main(String args[]){
        HelloWord helloWord =new HelloWord();
        helloWord.sayHello();
    }
}

编写AspectJ类,注意关键字为aspect(MyAspectJDemo.aj,其中aj为AspectJ的后缀),含义与class相同,即定义一个AspectJ的类

/**
 * Created by zejian on 2017/2/15.
 * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创]
 * 切面类
 */
public aspect MyAspectJDemo {
    /**
     * 定义切点,日志记录切点
     */
    pointcut recordLog():call(* HelloWord.sayHello(..));

    /**
     * 定义切点,权限验证(实际开发中日志和权限一般会放在不同的切面中,这里仅为方便演示)
     */
    pointcut authCheck():call(* HelloWord.sayHello(..));

    /**
     * 定义前置通知!
     */
    before():authCheck(){
        System.out.println("sayHello方法执行前验证权限");
    }

    /**
     * 定义后置通知
     */
    after():recordLog(){
        System.out.println("sayHello方法执行后记录日志");
    }
}

ok~,运行helloworld的main函数:

img

AspectJ的织入方式及其原理概要

aspect(切面)应用到目标函数(类)的过程,分为两种:

  • 动态织入(动态代理):Spring-AOP的动态代理。动态生成代理类。

    动态织入的方式是在运行时动态将要增强的代码织入到目标类中,这样往往是通过动态代理技术完成的,如Java JDK的动态代理(Proxy,底层通过反射实现)或者CGLIB的动态代理(底层通过继承实现),Spring AOP采用的就是基于运行时增强的代理技术

  • 静态织入(静态代理):ApectJ主要采用的是静态织入。把aspect类编译成class字节码后,在java目标类编译时织入编译器直接更改了目标类的字节码文件

img

编译后织入aspect类的HelloWord字节码反编译类:编译器直接更改了目标类的字节码文件。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.zejian.demo;

import com.zejian.demo.MyAspectJDemo;
//编译后织入aspect类的HelloWord字节码反编译类
public class HelloWord {
    public HelloWord() {
    }

    public void sayHello() {
        System.out.println("hello world !");
    }

    public static void main(String[] args) {
        HelloWord helloWord = new HelloWord();
        HelloWord var10000 = helloWord;

   try {
        //MyAspectJDemo 切面类的前置通知织入
        MyAspectJDemo.aspectOf().ajc$before$com_zejian_demo_MyAspectJDemo$1$22c5541();
        //目标类函数的调用
           var10000.sayHello();
        } catch (Throwable var3) {
        MyAspectJDemo.aspectOf().ajc$after$com_zejian_demo_MyAspectJDemo$2$4d789574();
            throw var3;
        }

        //MyAspectJDemo 切面类的后置通知织入 
        MyAspectJDemo.aspectOf().ajc$after$com_zejian_demo_MyAspectJDemo$2$4d789574();
    }
}

两者区别

原理区别
  • AspectJ 属于静态织入,原理是静态代理. 作用于编译期,字节码加载前,性能优于动态织入。直接更改字节码。

  • Cglib、JDK 动态代理属于动态织入,原理是动态代理。作用于运行期,字节码加载后,使用了反射,所以性能较差

首先说说静态织入:作用于编译期,字节码加载前,性能优于动态织入。AspectJ 用一种特定语言编写切面,通过自己的语法编译工具 ajc 编译器来编译,生成一个新的代理类,该代理类增强了业务类。

第二说下动态织入:作用于运行期,字节码加载后,使用了反射,所以性能较差。

Spring 底层的动态代理分为 Cglib 和 JDK 代理,为什么要分两种?

  1. JDK 动态代理用于对接口的代理,动态产生一个实现指定接口的类,注意动态代理有个约束目标对象一定是要有接口的,没有接口就不能实现动态代理,只能为接口创建动态代理实例,而不能对类创建动态代理。

  2. CGLIB 用于对类的代理,把被代理对象类的 class 文件加载进来,修改其字节码生成一个继承了被代理类的子类。注意,修改了字节码,所以需要依赖 ASM 包,使用 cglib 就是为了弥补动态代理的不足。

功能区别

AspectJ 更强大。例如Spring AOP不能修改final修饰的类。

相似点

那有的小伙伴可能有疑问,Spring AOP 使用了 AspectJ,怎么是动态代理呢?

Spring AOP只是借鉴了AspectJ的语法。

详细的区别如下:

img

cglib-AOP 和 Spring-AOP

jdk动态代理

流程

  1. 拿到被代理对象的引用。**并且获取到它的所有的接口,反射获取。**加入JDK Proxy 类 newInstance方法中
  2. JDK Proxy 类 newInstance方法生成被代理类。
  3. 动态生成 Java 代码,类中包含Invocationhandle的引用,重写invoke方法,在invoke方法中调用method.invoke()前后可以添加扩展的业务模块

cjlib动态代理

流程

  1. 拿到被代理对象的引用,生成一个继承自这个目标类的代理类一个目标类的FastClass和一个代理类的FastClass,总共生成三个class文件
  2. 代理类中重写了所有目标类的方法,并且添加每个方法的代理的方法
  3. 实际调用时,生成代理类与目标类的FastClass,FastClass中的机制是通过int类型数据定位需要调用的方法,这样省去了反射的调用。

CGLib 和 JDK 动态代理对比

  1. JDK 动态代理是实现了被代理对象的接口,CGLib 是继承了被代理对象。
  2. CGLib 代理的目标对象不需要实现任何接口,它是通过动态继承目标对象 实现的动态代理
  3. JDK 和 CGLib 都是在运行期生成字节码,JDK 是直接写 Class 字节码,CGLib 使用 ASM 框架写 Class 字节码,Cglib 代理实现更复杂,生成代理类比 JDK 效率低。
  4. JDK 调用代理方法,是通过反射机制调用,CGLib 是通过 FastClass 机制直接调用方法, CGLib 执行效率更高

运行时cglib效率为什么高(1.8之后目前差不多)

Cglib 采用了 FastClass 机 制:

  • 生成了两个FastClass,目标类的FastClass和一个代理类的FastClass
  • FastClass其实就一个数组,装入Method方法的引用。效率更快。(以空间换时间)

注:因为JDK动态代理中代理类中的方法是通过反射调用的,而cglib因为引入了FastClass,可以直接调用代理类对象的方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值