Aop面向切面编程
文章目录
- Aop面向切面编程
- 什么是AOP
- AOP术语
- Spring AOP 的使用
- 导入依赖
- 编写切面类
- 切面定义语法
- 小细节
- 输出日志成功
什么是AOP
AOP
:(Aspect Oriented Programming
)面向切面编程,和OOP(Object Oriented Programming
)面向对象编程一样,也是计算机开发的一种程序设计思想,与OOP将程序中的每个环节对象化,如实体类等相比,面向切面就是在不改变程序现有代码的前提下,可以设置某方法运行前或运行后新增额外代码的操作,减少对代码的入侵。包括过滤器、拦截器都是一种AOP的思想,只不过Spring AOP是Spring给的,(注:AOP并不是Spring框架独有的,而是从AspectJ框架中借鉴而来)过滤器是Java给的,拦截器是SpringMVC给的。
对AOP官方文档翻译:
AOP
目标是将横切关注点与业务主体进行进一步分离,以提高程序代码的模块化程度。通过在现有代码基础上增加额外的通知(Advice
)机制,能够对被声明为“切点“(Pointcut
)的代码块进行统一管理与扩展
AOP术语
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EqfMpLBJ-1688283328056)(../AppData/Roaming/Typora/typora-user-images/image-20230702134627970.png)]](https://static.555519.xyz/ab62/uploads/20230703/6d697b7cae1ca0f1f1801c2f5b5d97ec.jpg)
-
切面(Aspect
):是一个可以加入额外代码运行的特定位置,一般指方法之间的调用,可以在不修改原代码的情况下,添加新的代码,对现有代码进行升级维护和管理
-
织入(Weaving
):选定一个切面,利用动态代理技术,为原有的方法的持有者生成动态对象,然后将它和切面关联,在运行原有方法时,就会按织入之后的流程运行了
-
动态代理:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wOw6mwgq-1688283328057)(../AppData/Roaming/Typora/typora-user-images/image-20230702135722664.png)]](https://static.555519.xyz/ab62/uploads/20230703/fb0d1e6f7e028b21f5ea4c122ee2fc6c.jpg)
-
目标对象(Target
):需要被加强的业务对象
-
代理类(Proxy
) 一个类被AOP织入通知后,就产生了一个代理类。
-
切点(PointCut
):每个程序的连接点有多个,如何定位到某个感兴趣的连接点,就需要通过切点来定位。
-
连接点(Joinpoint
):程序执行的某个特定位置,如某个方法调用前,调用后,方法抛出异常后,这些代码中的特定点称为连接点。简单来说,就是在哪加入你的通知
- 连接点表示具体要拦截的方法,切点是定义一个范围,而连接点是具体到某个方法
-
通知(Advice
):原意为增强,是织入到目标类连接点上的一段程序代码。
分以下几种:
- 前置通知(
before
):执行业务代码前做些操作,比如获取连接对象
- 后置通知(
after
):在执行业务代码后做些操作,无论是否发生异常,它都会执行,比如关闭连接对象
- 异常通知(
afterThrowing
):在执行业务代码后出现异常,需要做的操作,比如回滚事务
- 返回通知(
afterReturning
):在执行业务代码后无异常,会执行的操作
- 环绕通知(
around
):
Spring AOP 的使用
导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
编写切面类
@Slf4j
@Aspect
@Component
public class LogAspect {
private HashMap<String, Object> map = new HashMap<>();
private final String filePath = "logFile.txt";
@Pointcut("execution(public * com.liner.controller.*.*(..))")
public void pointCut() {}
@Before("pointCut()")
public void beforeAdvice(JoinPoint joinPoint) {
log.info("----------- 前置通知 -----------");
Signature signature = joinPoint.getSignature();
log.info("返回目标方法的签名:{}", signature);
log.info("代理的是哪一个方法:{}", signature.getName());
Object[] args = joinPoint.getArgs();
log.info("获取目标方法的参数信息:{}", Arrays.asList(args));
map.put("方法签名", signature);
map.put("代理方法", signature.getName());
map.put("参数信息", Arrays.asList(args));
}
@After("pointCut()")
public void afterAdvice() {
log.info("----------- 后置通知 -----------");
}
@AfterReturning(pointcut = "pointCut()", returning = "keys")
public void afterReturningAdvice(JoinPoint joinPoint, String keys) {
log.info("~~~~~~~~~~ 后置返回通知 ~~~~~~~~~~");
log.info("后置返回通知的返回值:{}", keys);
}
@AfterThrowing(value = "pointCut()", throwing = "e")
public void afterThrowingAdvice(Exception e) {
log.info("~~~~~~~~~~ 后置异常通知 ~~~~~~~~~~");
log.info(e.toString());
}
@Around(value = "pointCut()")
public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {
log.info("----------- 环绕通知 -----------");
log.info("环绕通知的目标方法名:{}", proceedingJoinPoint.getSignature().getName());
map.put("目标方法", proceedingJoinPoint.getSignature().getName());
try {
return proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
} finally {
log.info("---------- 环绕通知结束 -------------");
writeLog(map);
}
return null;
}
void writeLog(HashMap<String, Object> map) {
try {
long currentTimeMillis = System.currentTimeMillis();
SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日-hh时mm分ss秒");
Date date = new Date(currentTimeMillis);
FileWriter fw = new FileWriter(filePath,true);
fw.write("----------- 环绕通知 -----------"+ format.format(date)+ "\r\n");
fw.write("环绕通知的目标方法名:" + (String) map.get("目标方法") + "\r\n");
fw.write("----------- 前置通知 -----------"+ "\r\n");
fw.write("返回目标方法的签名:" + (String) map.get("方法签名").toString() + "\r\n");
fw.write("代理的是哪一个方法:" + (String) map.get("代理方法") + "\r\n");
fw.write("获取目标方法的参数信息:" + (String) map.get("参数信息").toString() + "\r\n");
fw.write("----------- 最终通知 -----------"+ "\r\n");
fw.write("---------- 环绕通知结束 -------------"+ "\r\n");
fw.flush();
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
切面定义语法
@Pointcut("execution(public * com.liner.controller.*.*(..))")
下面介绍切面定义表达式的详细语法规则
语法模板:
execution(
modifier-pattern?
ret-type-pattern
declaring-type-pattern?
name-pattern(param-pattern)
throws-pattern?
)
带?
的是可选属性,不带?
是必须写的
-
modifier-pattern
:访问修饰符(可选)
-
ret-type-pattern
:返回值类型(必写),一般为*
-
declaring-type-pattern
:全路径类名(可选)
-
name-pattern(param-pattern)
:方法名(必写)
-
param-pattern
:参数列表(必写),一般为..
-
throws-pattern
:抛出的异常类型(可选)
小细节
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DVE36Unh-1688283328058)(../AppData/Roaming/Typora/typora-user-images/image-20230702143314638.png)]](https://static.555519.xyz/ab62/uploads/20230703/c25cb7ac8b868e2535efbcaa6f44399e.jpg)
几种通知图标不同:
-
前置通知(before
):
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gs7nXB4n-1688283328059)(../AppData/Roaming/Typora/typora-user-images/image-20230702143521568.png)]](https://static.555519.xyz/ab62/uploads/20230703/4f9d1cd6f377823fd34942078c1a7ce9.jpg)
-
后置通知(after
):
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bvYpIIpl-1688283328060)(../AppData/Roaming/Typora/typora-user-images/image-20230702143808539.png)]](https://static.555519.xyz/ab62/uploads/20230703/7c2498b455c67276d1f0ff9e6f60424e.jpg)
-
异常通知(afterThrowing
):
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NGPiH1E2-1688283328061)(../AppData/Roaming/Typora/typora-user-images/image-20230702144114022.png)]](https://static.555519.xyz/ab62/uploads/20230703/c6f10ddd538a92be3d4a6dd6a491382a.jpg)
-
返回通知(afterReturning
):
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BKLGGMgj-1688283328061)(../AppData/Roaming/Typora/typora-user-images/image-20230702144010730.png)]](https://static.555519.xyz/ab62/uploads/20230703/c644e1ecd73a290fc7d8fa81242b08d2.jpg)
-
环绕通知(around
):
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3NAaxAvc-1688283328062)(../AppData/Roaming/Typora/typora-user-images/image-20230702144223571.png)]](https://static.555519.xyz/ab62/uploads/20230703/aebe5c2671dfdecd03f9e6baeacd1a45.jpg)
输出日志成功
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YfcEvstE-1688283328062)(../AppData/Roaming/Typora/typora-user-images/image-20230702142807461.png)]](https://static.555519.xyz/ab62/uploads/20230703/7f11abc047e388acae7ffd22d2cddaf7.jpg)