讲解AOP代理和重点解析Spring AOP代理(动态代理),看完秒懂!!!!

上一章我们生动形象的讲了什么是AOP面向切面编程以及代码示例,有不明白的小伙伴可以去看看(https://blog.csdn.net/qq_32317661/article/details/82878679),承上启下,这一篇讲一下Spring AOP代理的两种方式和它的大致原理。

一、AOP代理

AOP代理主要分为静态代理和动态代理,静态代理的代表为AspectJ;而动态代理则以Spring AOP为代表。

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

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

举个实例的例子来说。首先我们有一个普通的Hello

1

2

3

4

5

6

7

8

9

10

public class Hello {

    public void sayHello() {

        System.out.println("hello");

    }

 

    public static void main(String[] args) {

        Hello h = new Hello();

        h.sayHello();

    }

}

使用AspectJ编写一个Aspect

1

2

3

4

5

6

7

public aspect TxAspect {

    void around():call(void Hello.sayHello()){

        System.out.println("开始事务 ...");

        proceed();

        System.out.println("事务结束 ...");

    }

}

这里模拟了一个事务的场景,类似于Spring的声明式事务。使用AspectJ的编译器编译

1

ajc -d . Hello.java TxAspect.aj

编译完成之后再运行这个Hello类,可以看到以下输出

1

2

3

开始事务 ...

hello

事务结束 ...

显然,AOP已经生效了,那么究竟AspectJ是如何在没有修改Hello类的情况下为Hello类增加新功能的呢?

查看一下编译后的Hello.class

1

2

3

4

5

6

7

8

9

10

11

12

13

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);

    }

}

可以看到,这个类比原来的Hello.java多了一些代码,这就是AspectJ的静态代理,它会在编译阶段将Aspect织入Java字节码中, 运行的时候就是经过增强之后的AOP对象。

1

2

3

4

5

public void ajc$around$com_listenzhangbin_aop_TxAspect$1$f54fe983(AroundClosure ajc$aroundClosure) {

        System.out.println("开始事务 ...");

        ajc$around$com_listenzhangbin_aop_TxAspect$1$f54fe983proceed(ajc$aroundClosure);

        System.out.println("事务结束 ...");

    }

从Aspect编译后的class文件可以更明显的看出执行的逻辑。proceed方法就是回调执行被代理类中的方法。

使用Spring AOP(重点掌握)

与AspectJ的静态代理不同,Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是在内存中临时为方法生成一个AOP对象(我们可以把它称之为‘代理对象’),这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。

Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。下面我们来依次讲解:

一、JDK动态代理

JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。现在都推荐面向接口编程,我们做的项目都是各种接口+实现类,所以是不是觉得这种代理方式和现在的接口编程很符合呢!

所以一个spring项目有接口和实现类,如果不在spring配置文件中特殊配置的话(就是默认配置),默认的动态代理方式就是JDK动态代理。但是,如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。

二、CGLIB动态代理

CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。

根据上述,应该能了解两种动态代理的原理了吧,我再举个例子:

JDK动态代理:

目标类(实现类)可以认作为厨师张三,张三能够具体实现接口的所有功能,比如老板让张三做饭,当AOP代理的时候,会根据张三所实现的接口,再创造一个张三A(代理对象)作为张三的分身,这时候张三和张三A具有相同的接口(多态的体现),两者长得一模一样,这时候张三A就可以在张三做饭之前把菜给洗干净了,然后张三本人来做饭。。但是在老板(调用者)看来,自始至终都是张三(目标类)一个人在洗菜做饭。

但是张三(目标类)知道,在他动手做饭之前他的代理对象帮他做了一些事情,代理对象也可以在他做饭之后帮他洗碗等等。

所以目标类要是没有实现接口,程序就不能根据接口再实现一个代理对象,也就不能代替目标类(实现类)去做一些事情。

这种代理方式,只有在通过接口调用方法的时候才会有效!

CGLIB代理:

我们把目标类比作李刚(化名),代理的时候,程序会根据制造一个子类来继承目标类,那么这个子类就是代理对象(李刚的儿子),所以李刚的儿子就可以能替他爸收钱(因为他爸是李刚哈哈),因为多态,所以程序识别不出来,然后目标类再替人办事,在外人看来,就是李刚在收钱办事。但是李刚有很多特权他儿子是没权限的,也就是目标类中有final方法,子类是无法继承的,那么这个代理对象就不能代理这部分功能。

三、如何选择spring动态代理的方式

上一章讲了如何配置springAOP,在spring配置文件中,有个配置如下

<aop:aspectj-autoproxy proxy-target-class="true" />

proxy-target-class默认是false,也就是默认使用JDK动态代理,但是如果目标类没有实现接口,会自动转为CGLIB代理;

设置为true,说明使用CGLIB代理!

 

本次讲解完了,下一章我们讲一下AOP代理和spring事务的联系,以及很重要的同一service中不同方法调用事务失效的问题!!

地址:https://blog.csdn.net/qq_32317661/article/details/82909045

 

 

 

 

  • 21
    点赞
  • 107
    收藏
    觉得还不错? 一键收藏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值