细说Spring——AOP详解(使用CGLIB实现AOP)

一、动态代理实现AOP的缺陷

在上一篇文章细说Spring——AOP详解(动态代理实现AOP)中讲解了如何使用动态代理实现AOP,虽然Java动态代理为我们提供了非常灵活的代理机制,但Java动态代理是基于接口的,如果目标对象没有实现接口我们该如何代理呢?这时候我们就需要使用CGLIB来实现AOP了。

二、CGLIB实现代理的原理

我们先创建一个目标对象

package demo1;

/**
 * Created by Yifan Jia on 2018/6/9.
 */
public class SomeService {
    public String doFirst() {
        System.out.println("执行doFirst()方法");
        return "abcde";
    }

    public void doSecond() {
        System.out.println("doSecond()方法");
    }

}

针对这个目标类,假如我们要使用动态代理实现AOP,那么我们只能在写一个增强的接口,然后让目标类实现增强接口,然后我们就可以使用动态代理实现目标类的增强,可是假如我们不想让目标类实现其他的接口,那么我们就只能使用CGLIB技术来实现目标类的增强了。
CGLIB实现目标类增强的原理是这样的:CGLIB会动态创建一个目标类的子类,然后返回该子类的对象,也就是增强对象,至于增强的逻辑则是在子类中完成的。我们知道子类要么和父类有一样的功能,要么就比父类功能强大,所以CGLIB是通过创建目标类的子类对象来实现增强的,所以:

目标子类 = 目标类 + 增强逻辑

至于CGLIB底层是如何动态的生成一个目标类的子类,它是使用动态字节码技术,我们知道我们编写的Java对象都是先编译为.class文件,然后由类加载器加载到内存中变为一个Java对象的,动态字节码技术就是通过转换字节码生成新的类来实现改变一个类的内部逻辑的。至于更基础的部分,我也没有深入的研究,有兴趣的可以自己研究一下。

三、使用CGLIB实现AOP

这里需要注意,我们需要导入CGLIB的相关包才能使用CGLIB,这里需要导入两个包:

  • cglib-nodep-3.2.0.jar:使用nodep包不需要关联asm的jar包,jar包内部包含asm的类.
  • cglib-3.2.0.jar:使用此jar包需要关联asm的jar包,否则运行时报错.

版本可以自行选择,我是有的Maven导入的jar包,依赖如下:

    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib-nodep</artifactId>
      <version>3.2.0</version>
    </dependency>

    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>3.2.0</version>
    </dependency>

通过CGLIB代理实现如下:

  • 首先实现一个MethodInterceptor,方法调用会被转发到该类的intercept()方法。
  • 然后在需要使用SomeService(目标对象)的时候,通过CGLIB动态代理获取代理对象。

我们仍然使用上面的SomeService作为目标对象,然后我们实现一个MethodInterceptor,在实现MethodInterceptor之前,我们先看一下这个接口是什么:

public interface MethodInterceptor extends Callback {
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable;
}

这里解释一下各个参数的意思:

  • Object为由CGLib动态生成的代理类实例
  • Method为上文中实体类所调用的被代理的方法引用
  • Object[]为参数值列表
  • MethodProxy为生成的代理类对方法的代理引用。

下面是实现的一个MethodInterceptor,同时写了创建增强对象的逻辑即myCglibCreator()方法,该方法返回增强对象。

public class CglibFactory implements MethodInterceptor {

    public SomeService myCglibCreator() {
        Enhancer enhancer = new Enhancer();
        //将目标类设置为父类,cglib动态代理增强的原理就是子类增强父类,cglib不能增强目标类为final的类
        //因为final类不能有子类
        enhancer.setSuperclass(SomeService.class);
        //设置回调接口,这里的MethodInterceptor实现类回调接口,而我们又实现了MethodInterceptor,其实
        //这里的回调接口就是本类对象,调用的方法其实就是intercept()方法
        enhancer.setCallback(this);
        //create()方法用于创建cglib动态代理对象
        return (SomeService) enhancer.create();
    }

    //回调接口的方法
    //回调接口的方法执行的条件是:代理对象执行目标方法时会调用回调接口的方法
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    
        Object result = methodProxy.invokeSuper(o, objects);

        //这里实现将返回值字符串变为大写的逻辑
        if(result != null) {
            result = ((String) result).toUpperCase();
        }
        return result;
    }

}

其中,Object result = methodProxy.invokeSuper(o, objects);调用代理类实例上的methodProxy方法的父类方法(即实体类SomeService中对应的方法),然后返回目标方法的返回值result,然后实现增强的逻辑,将返回的字符串变为大写的。下面是测试代码:

package demo1;

/**
 * Created by Yifan Jia on 2018/6/9.
 */
public class Test {
    public static void main(String[] args) {

        SomeService target = new SomeService();

        SomeService proxy = new CglibFactory(target).myCglibCreator();

        String result = proxy.doFirst();
        System.out.println(result);
        proxy.doSecond();
    }
}

结果截图:


这里写图片描述

上述代码中,我们通过CGLIBEnhancer来指定要代理的目标对象、实际处理代理逻辑的对象,最终通过调用create()方法得到代理对象,对这个对象所有非final方法的调用都会转发给MethodInterceptor.intercept()方法,在intercept()方法里我们可以加入任何逻辑,比如修改方法参数,加入日志功能、安全检查功能等;通过调用MethodProxy.invokeSuper()方法,我们将调用转发给原始对象,具体到本例,就是SomeService的具体方法。CGLIGMethodInterceptor的作用跟JDK动态代理代理中的InvocationHandler很类似,都是方法调用的中转站。

四、总结

我们学习了通过CGLIB实现动态增强,但是CGLIB也有其缺陷,那就是必须目标类必须是可以继承的,如果目标类不可继承,那么我们就无法使用CGLIB来增强该类,现在我们已经学习完了Spring AOP中两种AOP的实现机制,我们可以称JDK动态代理实现的AOP为面向接口的动态增强,将CGLIB实现的AOP称为面向子类的动态增强

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容

  • 本文主要讲实现AOP的 代理模式原理,以及静态代理,动态代理的区别和具体实现。 对SpringAOP的概念和使用,...
    _Zy阅读 746评论 0 1
  • **** AOP 面向切面编程 底层原理 代理!!! 今天AOP课程1、 Spring 传统 AOP2、 Spri...
    luweicheng24阅读 1,351评论 0 1
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • 前言 只有光头才能变强 上一篇已经讲解了Spring IOC知识点一网打尽!,这篇主要是讲解Spring的AOP模...
    Java3y阅读 6,878评论 8 181
  • 【关于嘉宾】大家好,我是胡文杰,一个热爱学习的小伙伴,3个标签介绍自己~ 1·终生学习实践者2·DISC心理学实践...
    持续行动家胡文杰阅读 411评论 0 3