在spring框架中,你直接输出一个由spring框架代理创建的对象,和我们自己new一个对象输出的对象的堆栈地址的形式是不同的.这种代理也是spring框架核心AOP的实现基础.
在spring框架中使用了两种代理方式:
1.JDK自带的动态代理.
2.Spring框架自己提供的CGLIB的方式.
这两种方式使用其实还是有区别的,打个比方吧.你有个哥哥他能通过内部买到春运的火车票,哪怕是最难买的火车票,他都有办法搞定.这个时候你的同事发现你有渠道能买到火车票,都想让你帮忙买票.
这个时候你可以从中抽成一张票20元,帮你的同事们代买火车票,你会把你的收到要买的票让你哥哥帮你完成,其实你只是从中多收了20元,真正的买票业务其实是你的哥哥帮你完成的,这个过程就是JDK动态代理做的事情,它是一个入口.真正的业务逻辑其实并不是通过你来完成,你做了你哥哥的代理,人们找你,真正的活还是你哥哥干,当然如果你也可以自己在不影响业务的情况下,增加一些其他的业务,比如例子中的收费.这其实就是AOP通过代理实现的原理,新增的业务是这样织入原有逻辑的.
另外一种方式就是,你也有这个门路,很熟悉你哥哥是如何操作的,自己收钱,自己再去操作亲身帮助同事们买票,这种方式是Spring自己的CGLIB的实现方式,他是生成了一个被代理类的子类,你也可以在子类中增加父类没有的功能.
当然你也能感受的到,第一种方式是一种接口实现,第二种是继承实现,这两种方式中接口实现是更加的灵活,
spring中proxy-target-class
在使用时这两种方式时,有不同的使用场景;
1.当被代理对象,实现了至少一个接口时会使用JDK动态代理.
2.当被代理对象没有实现任何接口时,spring会使用CGLIB.
当然这不是绝对的,你想让实现接口的对象用CGLIB代理加载,可以通过调整Spring的配置项来强行修改,让spring去使用某种方式生成Bean.当然会有弊端.
这里要说明强行使用CGLIB的两个要注意的点:
1.final修饰的方法无法被覆盖.
2.要将CGLIB的二进制发行包放在classpath下
为什么CGLIB不如JDK自带的动态代理方便还是要自己写一个代理方式呢?
其实CGLIB是比JDK动态代理更加高效的代码生成包,底层是依赖于ASM(开源的Java字节码编辑库,操作字节码),性能更好.