先聊下这么一种场景
    你写了一个类,里面提供了个对外方法。

    1. public interface MyService{
    2. void doService();
    3. }
    4. public interface MyServiceImpl implements MyService{
    5. @Override
    6. public void doService(){
    7. // do something
    8. }
    9. }
    10. // 调用方式
    11. public class Boot{
    12. public static void main(String[] args) {
    13. MyService myService = new MyServiceImpl();
    14. myService.doService();
    15. }
    16. }

    有一天你想在这个方法上加一种行为(比如:打印个日志、开启个事务、校验个权限),怎么做?很简单:

    1. public class MyServiceImpl implements MyService{
    2. private void action(){
    3. // some action
    4. }
    5. @Override
    6. public void doService(){
    7. action();
    8. // do something
    9. }
    10. }
    11. // 调用方式
    12. public class Boot{
    13. public static void main(String[] args) {
    14. MyService myService = new MyServiceImpl();
    15. myService.doService();
    16. }
    17. }

    如果有很多个类很多方法都需要加这个action呢?
    你可能会想到不要action强耦合在实现类里面,把这个action抽出来,这样其他类也用可以用了,而且修改抽出来的这个action,那么依赖的各个地方都能修改,完美!

    1. public class MyServiceImpl implements MyService{
    2. @Override
    3. public void doService(){
    4. ActionHolder.action();
    5. // do something
    6. }
    7. }
    8. public class ActionHolder{
    9. // 假设这个行为是个可静态化的方法。
    10. // 如果是个动态方法,调用方法还需要声明ActionHolder为成员变量
    11. public static void action(){
    12. // some action
    13. }
    14. }
    15. // 调用方式
    16. public class Boot{
    17. public static void main(String[] args) {
    18. MyService myService = new MyServiceImpl();
    19. myService.doService();
    20. }
    21. }

    完美吗?还能看到什么问题问题?你需要到每个方法里面都去加一句action()。工作增加只是一个方面,还有这个action在这个业务方法里面总感觉格格不入,因为它并不是我方法的内容,能不能别在我的方法里出现?当然功能我还是要的。

    OK,下面我们聊下什么是静态代理

    静态代理是一种常见的设计模式,比如存在一个Target类对外提供服务,通过设计一个TargetProxy类,该类和Target拥有一样的方法签名(一般通过实现同一个接口来保证),而且把Target类作为TargetProxy类的成员变量,这样在调用Target的服务时,可以用TargetProxy来代替,而在TargetProxy相关方法实现中,除了可以去调已经是作为成员变量的Target的服务之外,还可以自由在调用前或调用后进行拦截或改造。

    这个模式完美解决了代码侵入的问题,在业务方法中不用改动任何代码。如下:

    1. public class MyServiceImpl implements MyService{
    2. @Override
    3. public void doService(){
    4. // do something
    5. }
    6. }
    7. public class MyServiceImplProxy implements MyService{
    8. private MyServiceImpl myServiceImpl;
    9. @Override
    10. public void doService(){
    11. ActionHolder.action();
    12. myServiceImpl.doService();
    13. }
    14. }
    15. // 调用方式
    16. public class Boot{
    17. public static void main(String[] args) {
    18. MyService myService = new MyServiceImplProxy();
    19. myService.doService();
    20. }
    21. }

    可以看到,我们对MyServiceImpl什么代码都没改也获得了执行action的能力,但这是基于两点:

    1. 需要新增MyServiceImplProxy类
    2. 调用方需要使用的时候要创建MyServiceImplProxy而不是MyServiceImpl。

    这两点分别带来什么问题?
    第一点需要创建很多代理类,导致工作量问题还是没解决。
    第二点需要在注入的时候告诉它注入代理类而不是目标类。
    所以要么改动目标方法且可以直接使用目标类,要么创建代理类但需要使用代理类。

    我想说不管哪一种,反正都不想手动改那么多代码可以吗!
    可以!第一种,就是代码静态织入,相关的框架有aspectJ,提供了编织器,也就是说虽然不用手动改,但是在jvm编译器也就是在加载到方法区之前,就把相关代码编织到你的那个方法中去了。
    说到这里,有一个点相信大家很想问,但是我始终没有提的概念,那就是AOP,也就是面向方面编程,这是针对OOP思想的补充,为什么这么说呢,OOP的三大特性之一是什么?封装!软件设计的一大原则是什么?高内聚低耦合,也就是说理论上完美的OOP就是各自管各自的代码,我代码就是对象的行为的完全闭环的自描述,就是一个个烟囱竖起来,但是AOP实际是破坏了这种理论,但是带来的好处从上面的分析过程中不言而喻。它将一种横向的行为抽离出来进行统一管理,并又能注入回去。

    为什么在这里才提出来AOP呢?

    因为讲到了aspectJ,它和AOP有什么关系呢,因为在aspectJ框架中提供了一系列针对AOP联盟定义的概念的实现,AOP联盟就是为定义AOP概念和规范形成的联盟,在你的项目中的jar包依赖库中第一个肯定是aopalliance包。回到aspectJ,上面说到aspectJ能在编译期帮你把代码织入到方法中去,怎么织入呢。去实现aspectJ提供的一些规范,实际上你就算不看规范也大概能知道有什么,无非是哲学三连:切哪里?怎么切?切进去干什么?

    1. // 定义切面:切进去干什么
    2. @Aspect
    3. public class Aciotn{
    4. // 定义切点:切哪里
    5. @Pointcut("com.alibaba.taobao.MyService.doService(*)")
    6. public void pointcut(){
    7. }
    8. // 定义切方式:怎么切
    9. @Before("pointcut()")
    10. public void before(){
    11. }
    12. }

    至于定义完之后怎么配置,内部是怎么织入的,我也不知道,反正只知道有个aspectJ编织器。需要知道吗?你有兴趣可以去深入,我们这里不再讲下去,为什么?因为现在没人用了。那用什么?当然是spring-aop!

    aop、spring-aop、aspectJ、jdk-proxy、cglib到底TMD是什么关系?
    aop是编程思想,是形而上学,是哲学。
    spring-aop是aop思想的落地框架之一(基于代理类)。
    aspectJ是aop思想的落地框架之二(基于方法改造)。
    jdk-proxy是aop思想的落地工具之一(基于接口生成代理类),底层怎么生成代理类的?这是jdk提供的能力,你去看源码在生成代理类的时候最终调用了native方法,再下去就不清楚了。
    cglib是aop思想的落地工具之二(基于子类生成代理类),底层怎么生成代理类的?通过操作改动或生成class文件的字节码,你应该知道java的类数据都是从字节码加载的,所以能操作字节码有多NB就不用说了吧,生成个类还不是小菜儿?
    框架和工具是什么关系?你理解一下盖房子和吊机、推车、扳手这些是什么关系。一个是一整套解决方案,给你就能直接用的,比如防止盖好了能直接住的,一个是单纯的工具,给一个吊机、推车、扳手你不去盖房子的话有什么用?当然别杠!
    最重要的来了spring-aop和aspectJ是什么关系?spring-aop或者说spring框架的目的是为开发人员提供各种方便的应用搭建工具,为的是提升开发效率,spring无意去定义一个规范,别人弄好的能拿来就拿来,比如db数据操作spring就提供了mybatis、hibernate、jdbc原生等方式,当然这是不同轮子之间的替换。跟aop这还是有点区别,但思想是一样的,spring-aop就从aspectJ拿来了对AOP联盟定义一系列概念的实现。也就是刚才那几个注解。

    附录:

    image.png

    spring-aop会根据目标类的情况来选择用什么工具生成代理类,也可以通过下面方式指定(默认false):

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