Spring AOP
1.概念
Aspect Oriented Programing ⾯向切⾯编程。相对于oop面向对象编程来说,Aop关注的不再是程序代码中的某些类,某些方法,更多考虑的是一种面到面的切入(层与层之间的一种切入),所以被称为切面。
(servlet filter /* 的配置,实际上运用了aop的思想 )。
2.作用
AOP主要应⽤于⽇志记录,性能统计,安全控制,事务处理等⽅⾯,实现公共功能性的重复使⽤。
3.特点
1. 降低模块与模块之间的耦合度,提⾼业务代码的聚合度。(⾼内聚低耦合)
2. 提⾼了代码的复⽤性。
3. 提⾼系统的扩展性。(⾼版本兼容低版本)
4. 可以在不影响原有的功能基础上添加新的功能
4.底层实现
动态代理(JDK + CGLIB)
5.包含名词概念
5.1 Joinpoint(连接点)
被拦截到的每个点,
spring中指被拦截到的每⼀个⽅法,
spring aop⼀个连接点即代表⼀个⽅法的执⾏。
5.2 Pointcut(切⼊点)
对连接点进⾏拦截的定义(匹配规则定义 规定拦截哪些⽅法,对哪些⽅法进⾏处理),
spring 有专⻔的表达式语⾔定义。
5.3 Advice(通知)
拦截到每⼀个连接点即(每⼀个⽅法)后所要做的操作。
1. 前置通知 (前置增强)— before() 执⾏⽅法前通知
2. 返回通知(返回增强)— afterReturn ⽅法正常结束返回后的通知
3. 异常抛出通知(异常抛出增强)— afetrThrow()
4. 最终通知 — after ⽆论⽅法是否发⽣异常,均会执⾏该通知。
5. 环绕通知 — around 包围⼀个连接点(join point)的通知,如⽅法调⽤。这是最强⼤的⼀种通知类型。 环绕通知可以在⽅法调⽤前后完成⾃定义的⾏为。它也会选择是否继续执⾏连接点或直接返回它们⾃⼰的返回值或抛出异常来结束执⾏。
5.4 Aspect(切⾯)
切⼊点与通知的结合,决定了切⾯的定义。
切⼊点定义了要拦截哪些类的哪些⽅法,通知则定义了拦截过⽅法后要做什么。
切⾯则是横切关注点的抽象,与类相似,类是对物体特征的抽象,切⾯则是横切关注点抽象。
5.5 Target(⽬标对象)
被代理的⽬标对象。
5.6 Weave(织⼊)
将切⾯应⽤到⽬标对象并⽣成代理对象的这个过程即为织⼊。
5.7 Introduction(引⼊)
在不修改原有应⽤程序代码的情况下,在程序运⾏期为类动态添加⽅法或者字段的过程称为引⼊。
6.Spring AOP的实现
6.1. Spring AOP环境搭建
6.1.1 pom.xml文件:坐标依赖引⼊
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
<!--Spring AOP-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
6.1.2 添加spring.xml的配置
要点
添加命名空间
xmlns:aop=“http://www.springframework.org/schema/aop”
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
aop:aspectj-autoproxy/
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
6.2. 注解实现
6.2.1. 定义切⾯
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* 定义切面
* 切入点与通知的结合
* 切入点(@Pointcut):匹配规则。定义哪些类哪些方法需要被拦截
* 通知:方法拦截之后需要执行的操作
*/
@Component // 将类的实例化交给IOC容器
//@Aspect // 声明当前类是一个切面
public class LogCut {
/**
* 1. 切入点的定义
* 匹配规则。定义哪些类哪些方法需要被拦截
*
* @Pointcut("execution(匹配规则)")
* 例:@Pointcut("execution(* com.shsxt.service..*.*(..))")
* 第一位代表的是修饰范围,比如public、private等
* 第二位代表的是具体的包下的类的方法
*
* 匹配所有的public方法
* @Pointcut("execution(public *(..))")
* 匹配所有的set方法
* @Pointcut("execution(* set*(..))")
* 匹配指定包下的所有类的所有方法 (匹配service包下的所有类的所有方法)
* @Pointcut("execution(* com.shsxt.service.*(..))")
* 匹配指定包 及其子包下的所有类的所有方法 匹配service包下及其子包下的所有类的所有方法)
* @Pointcut("execution(* com.shsxt.service..*(..))")
*/
@Pointcut("execution(* com.shsxt.service..*.*(..))")
public void cut(){
}
/**
* 2. 通知的定义
*/
/**
* 前置通知
* 在目标方法执行前执行
*/
//@Before(value = "cut()")
public void before() {
System.out.println("前置通知...");
}
/**
* 返回通知
* 在目标方法正常返回后执行 (如果出现异常则不执行)
*/
//@AfterReturning(value = "cut()")
public void afterReturn() {
System.out.println("返回通知...");
}
/**
* 异常通知
* 当目标方法出现异常时执行
* @param e
*/
//@AfterThrowing(value = "cut()", throwing = "e")
public void afterThrow(Exception e) {
System.out.println("异常通知...异常信息:" + e.getMessage() );
}
/**
* 最终通知
* 当目标方法执行后执行 (无论执行过程是否出现异常都会执行)
*/
//@After(value = "cut()")
public void after() {
System.out.println("最终通知...");
}
/**
* 环绕通知
* 可以在目标方法执行前后,定义相应的处理
* 需要通过显式调用对应的方法,否则无法访问指定方法 (pjp.proceed();)
* @param pjp
*/
@Around(value = "cut()")
public void around(ProceedingJoinPoint pjp) {
try {
System.out.println("环绕通知-前置通知...");
// 调用该方法,目标对象才会执行
pjp.proceed();
System.out.println(pjp.getTarget() + "======" + pjp.getSignature());
// System.out.println("环绕通知-返回通知...");
} catch (Throwable e) {
e.printStackTrace();
System.out.println("环绕通知-异常通知...");
} finally {
System.out.println("环绕通知-最终通知...");
}
}
}
配置⽂件(spring.xml)
<!--配置AOP代理-->
<aop:aspectj-autoproxy/>
6.3. XML实现
6.3.1. 定义切⾯
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* 定义切面
* 切入点与通知的结合
* 切入点:匹配规则。定义哪些类哪些方法需要被拦截
* 通知:方法拦截之后需要执行的操作
*/
@Component // 将类的实例化交给IOC容器
public class LogCut02 {
/**
* 1. 切入点的定义
* 匹配规则。定义哪些类哪些方法需要被拦截
*
* @Pointcut("execution(匹配规则)")
* 例:@Pointcut("execution(* com.shsxt.service..*.*(..))")
* 第一位代表的是修饰范围,比如public、private等
* 第二位代表的是具体的包下的类的方法
*
* 匹配所有的public方法
* @Pointcut("execution(public *(..))")
* 匹配所有的set方法
* @Pointcut("execution(* set*(..))")
* 匹配指定包下的所有类的所有方法 (匹配service包下的所有类的所有方法)
* @Pointcut("execution(* com.shsxt.service.*(..))")
* 匹配指定包 及其子包下的所有类的所有方法 匹配service包下及其子包下的所有类的所有方法)
* @Pointcut("execution(* com.shsxt.service..*(..))")
*/
public void cut(){
}
/**
* 2. 通知的定义
*/
/**
* 前置通知
* 在目标方法执行前执行
*/
public void before() {
System.out.println("前置通知...");
}
/**
* 返回通知
* 在目标方法正常返回后执行 (如果出现异常则不执行)
*/
public void afterReturn() {
System.out.println("返回通知...");
}
/**
* 异常通知
* 当目标方法出现异常时执行
* @param e
*/
public void afterThrow(Exception e) {
System.out.println("异常通知...异常信息:" + e.getMessage() );
}
/**
* 最终通知
* 当目标方法执行后执行 (无论执行过程是否出现异常都会执行)
*/
public void after() {
System.out.println("最终通知...");
}
/**
* 环绕通知
* 可以在目标方法执行前后,定义相应的处理
* 需要通过显式调用对应的方法,否则无法访问指定方法 (pjp.proceed();)
* @param pjp
*/
public void around(ProceedingJoinPoint pjp) {
try {
System.out.println("环绕通知-前置通知...");
// 调用该方法,目标对象才会执行
pjp.proceed();
System.out.println(pjp.getTarget() + "======" + pjp.getSignature());
// System.out.println("环绕通知-返回通知...");
} catch (Throwable e) {
e.printStackTrace();
System.out.println("环绕通知-异常通知...");
} finally {
System.out.println("环绕通知-最终通知...");
}
}
}
配置⽂件(spring.xml)
<!--aop相关配置-->
<aop:config>
<!--aop切⾯-->
<aop:aspect ref="logCut02">
<!-- 定义aop 切⼊点 -->
<aop:pointcut id="cut" expression="execution(* com.xxxx.service..*.*(..))"/>
<!-- 配置前置通知 指定前置通知⽅法名 并引⽤切⼊点定义 -->
<aop:before method="before" pointcut-ref="cut"/>
<!-- 配置返回通知 指定返回通知⽅法名 并引⽤切⼊点定义 -->
<aop:after-returning method="afterReturn" pointcut-ref="cut"/>
<!-- 配置异常通知 指定异常通知⽅法名 并引⽤切⼊点定义 -->
<aop:after-throwing method="afterThrow" throwing="e" pointcut-ref="cut"/>
<!-- 配置最终通知 指定最终通知⽅法名 并引⽤切⼊点定义 -->
<aop:after method="after" pointcut-ref="cut"/>
<!-- 配置环绕通知 指定环绕通知⽅法名 并引⽤切⼊点定义 -->
<aop:around method="around" pointcut-ref="cut"/>
</aop:aspect>
</aop:config>
7.Spring AOP总结
7.1. 代理模式实现三要素
- 接⼝定义
- ⽬标对象与代理对象必须实现统⼀接⼝
- 代理对象持有⽬标对象的引⽤ 增强⽬标对象⾏为
7.2. 代理模式实现分类以及对应区别
- 静态代理:⼿动为⽬标对象制作代理对象,即在程序编译阶段完成代理对象的创建
- 动态代理:在程序运⾏期动态创建⽬标对象对应代理对象。
- jdk动态代理:被代理⽬标对象必须实现某⼀或某⼀组接⼝ 实现⽅式 通过回调创建代理对象。
- cglib 动态代理:被代理⽬标对象可以不必实现接⼝,继承的⽅式实现。
- 动态代理相⽐较静态代理,提⾼开发效率,可以批量化创建代理,提⾼代码复⽤率。
7.3. Aop 理解
- ⾯向切⾯,相⽐oop 关注的是代码中的层 或⾯
- 解耦,提⾼系统扩展性
- 提⾼代码复⽤
7.4. Aop 关键词
- 连接点:每⼀个⽅法
- 切⼊点:匹配的⽅法集合
- 切⾯:连接点与切⼊点的集合决定了切⾯,横切关注点的抽象
- 通知:⼏种通知
- ⽬标对象:被代理对象
- 织⼊:程序运⾏期将切⾯应⽤到⽬标对象 并⽣成代理对象的过程
- 引⼊:在不修改原始代码情况下,在程序运⾏期为程序动态引⼊⽅法或字段的过程