美文网首页
AOP横扫千军-概念理解

AOP横扫千军-概念理解

作者: 现实目标决心 | 来源:发表于2018-08-11 23:30 被阅读0次

    在了解AOP之前,我们先来总览一下Spring生命之树。


    Spring的生命之树

    前面几章节我们主要对IoC容器进行了研究,这里先回顾一下。

    • IoC容器就像是通灵之术,我们可以在需要的时候,将对象召唤出来。
    • 在召唤之前,我们要创建并将对象放入容器,处理各种依赖绑定关系。 这个工作可以通过BeanFactory和ApplicationContext来做。
    • ApplicationContext实现了BeanFactory,还有统一资源管理、国际化、事件发布等功能。

    有了以上大树干的知识后,我们继续往上探索。这次,我们来到了Spring AOP分支。

    0x01 为什么要有AOP

    软件开发一直在寻求更加高效、更易维护、更易扩展的方式。为了提交开发效率,我们不断对语言进行了抽象。汇编是对机器语言二进制的抽象,C语言是对汇编语言的抽象封装。为了便于维护和扩展,我们就对某些相同的功能进行归类和模块化,走过了从过程化编程到面向对象编程OOP(Object-Oriented Programming)的“短暂而漫长”的历程。

    OOP擅长的是将业务进行抽象,然后封装、模块化。但是有一些场景使用OOP去处理就显得非常吃力。例如,散落在各个模块的日志记录、安全检查、异常处理、事务管理等。这些相同的功能,如果分散在各个模块,那维护起来简直是噩梦。

    加入各种横切需求后的系统模块关系图

    我们需要找到能够简洁处理以上这些问题的编程思想,于是AOP(Aspect-Oriendted Programming)就呼之欲出了。

    0x02 AOP走向现实的方案

    上面说的只是AOP的由来以及概念。有了概念之后,我们还需要让AOP走向现实,在开发的时候能够用上。在java编程体系上,有两种方法可以实现AOP功能:

    • 静态AOP(一代),在编译时,将切面信息写到类中。
    • 动态AOP(二代),在系统运行、类加载期间,对字节码进行操作,来织入切面信息。

    两种方式各有利弊,静态方式性能好但不灵活,动态方式灵活但有损性能。随着硬件资源的提升,性能的问题得到解决,灵活变得越来越重要。

    所有的Java程序的class都要通过相应的类加载器加载到Java虚拟机之后,才可以运行。默认的类加载器会读取class字节码文件,然后按照class字节码规范,解析并加载这些class文件到虚拟机运行。如果我们能够在这个class文件加载到虚拟机运行期间,将横切逻辑织入到class文件的话,就完成了AOP和OOP的融合。AspectJ项目框架就是采用这种方式实现的。

    0x03 AOP国家的公民--学习AOP必须掌握的概念

    Joinpoint:织入点

    在系统运行之前,AOP功能模块都需要织入到OOP功能模块中。这些将要在其之上进行织入操作的系统执行点就称为Joinpoint。

    Joinpoint是一个描述织入点的一个概念,方便表达沟通,不会在程序中体现。基本上,程序执行过程中你认为必要的执行时点都可以作为Joinpont。常见的Joinpoint有方法调用、构造方法调用、字段设置获取、异常处理执行、类初始化等

    Pointcut -- Joinpoint的表达方式

    Pointcut 是 Joinpoint的表达方式,表示织入点的具体位置,通过Pointcut 我们才知道往哪些织入点织入横切逻辑。

    Pointcut 的表述方式很重要,需要在程序里面体现的:

    • 1 直接指定Joinpoint所在方法名。
    • 2 使用正则表达式。
    • 3 使用特定的Pointcut 表述语言。
    • 4 Pointcut的逻辑运算&& 和 || 。

    举栗子:
    1)execution(* (..))
    表示匹配所有方法
    2)execution(public * com. savage.service.UserService.
    (..))
    表示匹配com.savage.server.UserService中所有的公有方法
    3)execution(* com.savage.server...(..))
    表示匹配com.savage.server包及其子包下的所有方法
    除了execution表示式外,还有within、this、target、args等Pointcut表示式。一个Pointcut定义由Pointcut表示式和Pointcut签名组成

    @Pointcut("execution(* com.savage.aop.MessageSender.*(..))")
    private void logSender(){
    }
    @Pointcut("execution(* com.savage.aop.MessageReceiver.*(..))")
    private void logReceiver(){}
    @Pointcut("logSender() || logReceiver()")
    private void logMessage(){}
    

    Advice

    Advice是单一横切关注点逻辑的载体,它代表将要织入到Joinpoint的横切逻辑,相当于class中的method。也就是说,我们要实现的逻辑代码是放在Advice中去实现的。

    Advice可以分为多种具体的形式:

    Before Advice: 在Joinpoint指定位置之前执行横切逻辑。
    After Advice: 在Joinpoint指定位置之后执行横切逻辑。
    Around Advice: 包裹了Joinpoint指定位置的前后,最常用的Advice。通常叫做拦截器(Interceptor)。
    Introduction Advice: 可以为原有对象添加新的特性或者行为。

    Aspect

    Aspect是对系统中横切关注点逻辑进行模块化封装的AOP概念实体,相当于OOP的class。通常情况下,Aspect可以包含多个Pointcut和相关的Advice定义。

    下面来一个简单的示例,结合上面讲的概念来理解一下切面:

        package com.sxit;  
        import org.aspectj.lang.ProceedingJoinPoint;  
        import org.aspectj.lang.annotation.After;  
        import org.aspectj.lang.annotation.AfterReturning;  
        import org.aspectj.lang.annotation.AfterThrowing;  
        import org.aspectj.lang.annotation.Around;  
        import org.aspectj.lang.annotation.Aspect;  
        import org.aspectj.lang.annotation.Before;  
        import org.aspectj.lang.annotation.Pointcut;  
          
        @Aspect  //Aspect是对系统中横切关注点逻辑进行模块化封装的AOP概念实体,相当于OOP的class
        public class AspectStyle {  
           // Pointcut表示织入点的具体位置,通过Pointcut 我们才知道往哪些织入点织入横切逻辑
            @Pointcut("execution(* com.sxit..*.*(..))")  
            public void init(){  
                  
            }  
          //Advice是单一横切关注点逻辑的载体,它代表将要织入到Joinpoint的横切逻辑
            @Before(value="init()")  
            public void before(){  
                System.out.println("方法执行前执行.....");  
            }  
              
            @AfterReturning(value="init()")  
            public void afterReturning(){  
                System.out.println("方法执行完执行.....");  
            }  
              
            @AfterThrowing(value="init()")  
            public void throwss(){  
                System.out.println("方法异常时执行.....");  
            }  
              
            @After(value="init()")  
            public void after(){  
                System.out.println("方法最后执行.....");  
            }  
              
            @Around(value="init()")  
            public Object around(ProceedingJoinPoint pjp){  
                System.out.println("方法环绕start.....");  
                Object o = null;  
                try {  
                    o = pjp.proceed();  
                } catch (Throwable e) {  
                    e.printStackTrace();  
                }  
                System.out.println("方法环绕end.....");  
                return o;  
            }  
        }
    

    0x04 织入和织入器

    上面描述的都是AOP的概念,是独立于OOP编程模型的。只有经过织入过程之后,以Aspect模块化的横切关注点才会集成到OOP现存系统中。而完成织入过程的那个“人”就称为织入器(Weaver)啦。ProxyFactory类是Spring AOP中最常用的织入器。

    AOP织入

    小结

    本文主要介绍了AOP的概念,先对AOP的世界来一个整体的理解。在真正的使用过程中,不至于迷茫。

    相关文章

      网友评论

          本文标题:AOP横扫千军-概念理解

          本文链接:https://www.haomeiwen.com/subject/wfhjbftx.html