AOP
-
AOP: Aspect-oriented programming. 面向切面的程序设计
- 将横切关注点进一步分离,以提高代码的模块化程度
- 在现有的项目代码的基础上增加额外的通知Advice机制,能够对声明为切点Pointcut的逻辑功能代码块进行统一的管理与装饰
- 横切关注点: 在项目的多个模块中都有出现的逻辑功能代码
- 面向切面的程序设计可以将与代码核心业务逻辑联系较弱的功能添加到程序中,同时保证业务代码的可读性
-
AOP核心思想:
-
从核心关注点分离出横切关注点是面向切面程序设计的核心
- 分离关注点使得解决特定领域问题的代码从业务逻辑中独立出来
- 业务逻辑中不再含有针对特定领域问题代码的调用
- 业务逻辑和特定领域问题的关系通过切面来进行封装和维护
-
从核心关注点分离出横切关注点是面向切面程序设计的核心
-
AOP和OOP的比较:
-
OOP: 面向对象的程序设计
- OOP引入了封装,继承和多态来建立一个对象层次结构来模拟公共行为的集合.但是OOP无法为分散的对象引入公共行为
- OOP适用于定义对象自上而下的关系,不适合定义对象从左到右的关系
-
AOP: 面向切面的程序设计
- 使用横切的技术,分解封装对象的内部,将影响多个类的公共行为封装到一个可重用的模块称作切面
- AOP通过横切的关系将与业务无关,但是被业务模块共同调用的逻辑和责任封装起来,减少项目中的重复代码,降低模块之间的耦合度,提升项目的操作性和维护性
-
OOP: 面向对象的程序设计
-
AOP中的基本概念:
-
切面: AspectJ
- 模块化的横切关注点.也就是横向分散在多个功能模块中的特定逻辑功能
-
连接点: Join point
- 在项目执行过程中能够在项目中插入切面功能的项目中的某一个点
- Spring AOP中Joinpoint指代的是所有方法的执行点
- 这个点可以是项目中某个方法调用前,调用后,方法抛出异常后等等
- 切面利用这些连接点切入到正常的项目流程中,添加某些行为
-
AOP中的Joinpoint可以有多种类型:
- 构造方法调用
- 字段的设置和获取
- 方法的调用
- 方法的执行
- 异常处理的执行
- 类的初始化
- 注意: Spring中只支持执行方法类型的joinpoint
-
AOP中的Joinpoint可以有多种类型:
-
通知: Advice
- AOP框架在特定的连接点处执行的动作
-
Spring AOP中提供5种类型的通知Advice:
- 前置通知: Before. 在目标方法调用之前执行通知功能
- 后置通知: After. 在目标方法调用完成之后执行通知功能.无论目标方法是否发生异常,后置通知都会执行
- 后置返回通知: After-returning. 在目标方法返回值之后执行通知功能
- 后置异常通知: After-throwing. 在目标方法抛出异常之后执行通知功能
- 环绕通知: Around. 在目标方法调用之前和调用之后执行通知功能
-
切点: Pointcut
- 执行通知的一系列连接点的集合
- Pointcut是一个描述信息,用于修饰Joinpoint, 通过Pointcut,AOP就可以确定哪些Joinpoint可以编织进入Advice
- AOP通过切点定位到特定的连接点
- 一个切点匹配多个连接点
-
引入: Introduction
- 添加方法或者字段到被通知的类
- Spring支持引入接口到任何被通知的对象中
- Spring中使用Introduction, 可以通过DelegatingIntroductionInterceptor来实现通知,通过DefaultIntroductionAdvisor来配置Advice和代理类要实现的接口
-
目标对象: Target Object
- 包含连接点的对象.也就是被通知或者被代理的对象
-
AOP代理: AOP Proxy
- AOP框架创建的对象,包括通知
- Spring中的AOP代理包括JDK动态代理和CGLIB代理
-
织入: Weaving
- 织入描述的是将切面应用到目标对象来创建新的代理对象的过程
- Spring AOP使用了动态代理技术,在项目运行时织入切面
- Spring AOP支持使用两种方式JDK动态代理和CGLIB代理生成代理对象.默认策略是如果目标类为接口,则使用JDK动态代理,如果目标类是一个类对象则使用CGLIB代理
- 织入描述的是将切面应用到目标对象来创建新的代理对象的过程
-
切面: AspectJ
-
AOP实现的两种方式:
- 动态代理方式: 利用拦截方法,对拦截方法进行装饰,取代原有方法的执行
- 静态织入方式: 引入特定的语法创建切面,使得编译器在编译期间织入切面的代码
-
AOP使用的四种方式:
- 基于代理模式的AOP使用
- @AspectJ注解驱动的切面
- POJO切面,通过 <aop:config /> 配置切面
- 注入式AspectJ切面,通过 <aop:aspectj-autoproxy /> 配置切面
-
Spring AOP的使用方法有以下三个步骤:
- 定义增强方法,并在方法里使用AspectJ的注解标注Spring在何处调用方法
- 在横切方法上使用注解 @Component和 @Aspect
- 在 @Configuration配置类上使用注解 @EnableAspectJAutoProxy
- Spring AOP中XML配置使用示例
- SpringBoot AOP中注解式使用示例
-
AOP中的切入点表达式: 切入点表达式用来指定切入点的目标
execution([modifier-pattern]? [return-type-pattern] [declaring-type-pattern]? [name-pattern]([param-pattern])[throws-pattern]?)
- modifier-pattern: 修饰符匹配
- return-type-pattern: 返回值匹配
- declaring-type-pattern: 类路径匹配
- name-pattern: 方法名匹配
- param-pattern: 参数匹配
- throws-pattern: 异常类型匹配
-
? : 表示该匹配项是可选项
-
示例:
- 匹配所有方法:
execution(* *(..))
- 匹配com.oxford.service包下面的所有公共方法:
execution(public * com.oxford.service.*(..))
- 匹配com.oxford.service包以及子包下面的所有方法:
execution(* com.oxford.service..*.*(..))
-
示例:
- 使用 @Pointcut定义切点时,可以使用 &&, || 和 ! 逻辑运算符
面向切面的基础
-
面向切面程序设计的核心概念就是从核心关注点中分离出横切关注点:
- 分离关注点使得解决特定领域问题的逻辑功能代码从业务逻辑代码中独立出来
- 业务逻辑代码中不再含有对特定领域问题的逻辑功能代码的调用
- 业务逻辑代码同对特定领域问题的逻辑功能代码的关系通过切面来进行封装和维护
-
关注点: Concern
- 对软件工程有意义的,小的,可管理的,可描述的软件组成部分
- 一个关注点通常只和一个特定概念或者目标相关联
-
主关注点: Core Concern
- 一个软件中的最主要的关注点
-
关注点分离: Separation of concerns, SOC
- 标识,封装和操作关注点的能力
-
方法: Method
- 用来描述,设计和实现一个给定关注点的软件构造单位
-
横切: Crosscut
- 如果实现两个关注点方法存在交集,那么这两个关注点相互横切
-
支配性分解: Dominant decomposition
- 将软件分解成模块的主要方式
- 传统的程序设计语言以一种线性的文本来描述软件,只采用一种方式,比如类来将软件分解成模块
- 这样使得某些关注点比较好捕捉,容易进一步组合,扩展
- 但是仍然会存在一些关注点没有被捕捉,弥撒在整个软件内部
- 支配性分解通常是按照主关注点进行模块分解的
- 将软件分解成模块的主要方式
-
横切关注点: Crosscuting concerns
- 在传统的程序设计语言中,除了主关注点可以被支配性分解的方式捕捉外,仍然有许多没有被支配性分解方式捕捉到的关注点
- 这些关注点弥散在整个软件内部,这些关注点同主关注点是横切的
-
切面: Aspect
- 支配性分解的基础上提供的一种辅助的模块化机制
- 切面的模块化机制可以捕捉横切关注点
AOP原理剖析
在这里插入图片描述-
后置处理器根据Bean是否配置Advisor判断是否需要进行动态代理
-
如果需要进行动态代理,那么就构造创建ProxyFactory对象
-
通过ProxyFactory对象的父类中的getAopProxyFactory() 方法获取AopProxyFactory对象,默认获取的是DefaultAopProxyFactory
-
通过AopProxyFactory工厂createAopProxy() 方法创建实际的代理对象
-
ProxyBeanFactory:
-
Spring使用工厂模式创建每一个Proxy. 每一个Class类型,都会有一个相对应的ProxyFactoryBean
- 对于生成Proxy的工厂而言,只需要知道对应的Advice信息即可
- Advice信息维护在Advised中 ,Advised可以根据特定的类名和方法名返回对应的AdviceChain, 来表示需要执行的Advice串
-
JDK动态代理:
-
JDK动态代理是面向接口的JdkDynamicAopProxy生成代理对象
- 溯源到JDK动态代理的字节码文件.在反编译的代码文件中可以发现代理类已经是继承了Proxy类,因为Java是单继承多实现的,所以不能再继承其余的类,但是代理类与目标类之间必须建立一种关系来保证代理对象可以被引用到,这样就只能实现接口
- 目标类实现了至少一个接口时,那么代理类就可以通过实现与目标类相同的接口,使用接口类型的变量接收代理类的实例
- JdkDynamicAopProxy类实现了AopProxy, 返回Proxy, 并且自身也实现了InvocationHandler
- 也就是说,当我们使用proxy时,对proxy对象调用的方法,都会转到这个类的invoke() 方法中
-
JDK动态代理是面向接口的JdkDynamicAopProxy生成代理对象
-
Cglib代理:
- 基于Cglib子类继承方式CglibAopProxy生成代理对象
Spring AOP代理选择 | 代理描述 | 实现机制 | 优点 |
---|---|---|---|
JDK动态代理 | 1. 通过反射接受被代理的类,并且要求被代理的类必须实现一个接口2. JDK动态代理的核心是InvocationHandler接口和Proxy类 | 通过Java的内部反射机制实现 | 反射机制在生成类的过程中非常高效 |
CGLIB代理 | 1. 通过继承的方式动态生成目标类的代理2. 通过修改字节码的方式来实现代理,可以在运行时动态的生成某个类的子类3. 如果某个类使用final修饰,那么无法使用CGLIB做动态代理4. CGLIB代理的核心是Enhancer类 | 使用ASM框架实现. ASM框架是一种可以操作字节码的框架 | ASM在类生成之后的执行过程中非常高效 |
网友评论