背景
地点:长沙飞思
时间:2023/11/13~17
大致内容:AOP
前言
AOP,即面向切面编程,是OOP的延续,不改变目标方法代码前提,增强目标方法。是Spring框架中的一个重要内容,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP
AOP 采取横向抽取机制(动态代理),取代了传统纵向继承机制的重复性代码,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面。
主要作用是分离功能性需求和非功能性需求,使开发人员可以集中处理某一个关注点或者横切逻辑,减少对业务代码的侵入,增强代码的可读性和可维护性。
Spring Aop
基于AspectJ的AOP实现
专业术语
Aspect(切面):Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及 相应的 Advice。
Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及 异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过 通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地 方。
Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、 after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
Target(目标对象):织入 Advice 的目标对象。
Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程。
切入点
切入点表达式: 方法签名描述
方法签名: 访问修饰符 返回值类型 [包.类.]方法名(参数列名) throws 异常声明;
语法:
execution(修饰符? 返回值 方法名(参数) 异常?)
注意:
?: 0或者1个
通配符:
*
: 任意 返回值, 方法名, 类名
..
: 任意 包中使用:..
: 表示该包,以及该包后代包 参数:..
: 任意个数,任意类型的参数
execution(* *(..)) 匹配所有spring管理对象所有方法, 第一个*任意返回值 ,第二个*任意方法, .. 任意参数
execution(* org.suke.spring..*.*(..) ):匹配org.suke.spring包及其子包所有类的所有方法
execution(* org.suke.spring.*.*(..) ):匹配org.suke.spring包所有类的所有方法
execution(* org.suke.spring.*.add*(..) ):匹配org.suke.spring包所有类的所有以add开头的方法
execute( public * addUser(entity.User)) 匹配addUser方法,返回值任意,参数为entity包User对象
execute( public void *(entity.User)) 匹配返回值为void,参数为entity包User的所有方法
execute( public void addUser(..)) 匹配返回值为void,参数任意的addUser方法
使用
依赖导入
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.version>5.2.15.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<!--导入第三方的aspectj框架-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.36</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</project>
通知类型
-
前置通知/增强
-
后置通知
-
异常通知
-
最终通知
-
环绕通知
基于xml
<bean id="userService" class="com.fs.service.impl.UserServiceImpl"></bean>
<bean id="logAdvice" class="com.fs.advice.LogAdvice"></bean>
<aop:config>
<!--织入-->
<!--配置切入点: 对那些类那些方法进行增强
id: 唯一标识符
expression: 切入点表达式
-->
<aop:pointcut id="myPointcut" expression="execution(* com.fs.service..*.*User(..))"/>
<!--织入
ref: 增强类bean对象
-->
<aop:aspect ref="logAdvice">
<!-- <aop:before method="before" pointcut-ref="myPointcut" />-->
<!--
后置增强:正常执行
属性:
returning: 目标方法返回值传递给增强方法那个参数, 接收返回值的参数名
-->
<!-- <aop:after-returning method="afterReturn" pointcut-ref="myPointcut" returning="rs" />-->
<!--异常增强: 目标方法产生异常
throwing: 目标方法执行产生异常对象被增强方法那个参数接收
-->
<!-- <aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut" throwing="ex"/>-->
<!-- <aop:after-throwing method="afterThrowing2" pointcut-ref="myPointcut" throwing="ex"/>-->
<!--最终增强配置-->
<!-- <aop:after method="after" pointcut-ref="myPointcut"/>-->
<!--环绕增强-->
<aop:around method="around" pointcut-ref="myPointcut"/>
</aop:aspect>
</aop:config>
基于注解
在增强方法上提供相关的注解:
@Before: 前置增强配置
@AfterThrowing: 异常增强配置
@AfterReturning: 后置增强配置
@After: 最终增强配置
@Around: 环绕增强配置
@Aspect: 标记类是一个通知类
可以把切入点表达式定义在一个方法上, 在其他增强方法上, 切入点表达式写到方法名()
总结
仰天大笑出门去,我辈岂是蓬蒿人