AOP横扫千军-概念理解

2018-08-11  本文已影响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的世界来一个整体的理解。在真正的使用过程中,不至于迷茫。

上一篇下一篇

猜你喜欢

热点阅读