本文共 3334 字,大约阅读时间需要 11 分钟。
思维导图:
一.引言
在我们所编写的业务代码中,我们总是不得已的在业务逻辑中添加一些其他的功能代码,比如打印日志,事务处理等等.我们将这些散布于应用中多处的功能称之为横切关注点.在业务代码中掺杂这些关注点的功能会让代码显得不纯粹和复杂.所以,这就是面向切面编程AOP出现的原因:将横切关注点和业务逻辑相分离.横切关注点也可以被模块化为特殊的类:切面.
在本文中,第一部分介绍了什么是面向切面编程,包括AOP的专业术语和Spring对AOP的支持.第二部分则通过介绍如何定义切点,切面,通知等来使用AOP.
二.什么是面向切面编程
2.1 AOP术语
- 连接点:在应用执行中能够插入切面的一个点.
- 通知:定义了切面是什么以及何时使用
- 切点:匹配通知所切入的连接点,即切面会在何处使用
- 切面:通知和切点的集合,在何时何处完成何种功能
- 引入:引入允许我们想现有类添加新的方法或属性
- 织入:把切面引入到目标对象并创建新的代理对象的过程
用一个实例来说明以上的部分术语,现在有个类Person,他定义了人类的所有行为.每种行为都有一个方法与之的对应.我们可以在这些行为方法之前或者之后进行我们需要的处理,比如玩游戏之前需要打印日志.这些需要公共的横切关注点功能的地方就是连接点.即可以进行AOP处理的地方.通知则定义了我们究竟要对人类的某一个行为在其之前或者之后做什么样的处理.切点即是这次需要对哪一个具体的行为进行处理,它确定了要处理那些连接点.最后,切面即是由切点和通知所共同定义.确定了要对Person的那些方法在什么时候做什么样的处理.
2.2 Spring中的AOP
spring的经典AOP只支持对普通方法(非构造方法)的调用拦截.其原理是基于动态代理的,所以会创建一个目标对象的代理,此代理会依据通知处理此次拦截,知道需要被代理的bean时才会创建代理对象.
当然,我们也可以在Spring中使用注入式AapectJ切面,他可以实现例如构造器和属性的拦截.
三.切面的使用
切面由两个要素构成:切点和通知.通知决定了在什么时候做什么事,而切点决定了在哪里做.
我们可以在两种地方定义切点,一种是定义一个全局的切点,以便通知使用时直接调用.一种是在通知中定义切点,下入是切点的定义格式:
通知的定义则有五种类型,如下图:
以下是代码部分的实现:
- 方法被拦截的类
/** * 用于测试AOP的被拦截类 * * @author : zhouhao * @date : Created in 2019/3/11 19:40 */@Componentpublic class Person { /** * 无输入参数 * @author : zhouhao * @date : 2019/3/12 6:20 */ public void sleep(){ System.out.println("I am sleeping"); } /** * 有输入参数 * @author : zhouhao * @date : 2019/3/12 6:20 */ public void say(String words){ System.out.println(words); }}
- 定义切面
/** * Person类的AOP处理切面 * 使用@Aspect定义此类为一个切面 * 定义切面需要两个关键元素:切点和通知 * !!!注意在自动扫描中,切面也需要使用@Component注解 * * @author : zhouhao * @date : Created in 2019/3/11 19:42 */@Aspect@Componentpublic class PersonSection { /** * 定义一个切点,以便通知直接使用 * 定义格式为 execution(返回值 方法路径(方法参数) ) * 如果需要对所拦截的方法参数进行处理则可以使用 args获取参数 * * @param words 被拦截方法的入参 * @author : zhouhao * @date : 2019/3/12 6:25 */ @Pointcut("execution(* com.zhouhao.springstudy.action.Person.say(String)) && args(words)") public void sayPoint(String words){ } /** * 定义通知,此通知使用已定义好的切点进行拦截 * 通知类型为Before,表示在被拦截方法调用之前调用 * * * @param words 被拦截方法的入参 * @author : zhouhao * @date : 2019/3/12 6:29 */ @Before(value = "sayPoint(words)") public void beforeSay(String words){ System.out.println("before say : " + words); } /** * 也可以在定义通知的时候直接定义切点,(..)表示任意类型数量的入参 * 通知类型为After,表示在被拦截方法调用之后调用 * * @author : zhouhao * @date : 2019/3/12 6:33 */ @After("execution(* *.say(..))") public void afterSay(){ System.out.println("after say"); } /** * 定义环绕型通知,在此方法中如果可以使用joinPoint.proceed()表示对被拦截方法的调用 * 如果不进行调用,则表示拦截的方法被阻塞了. * @param joinPoint 切入点 * @author : zhouhao * @date : 2019/3/11 20:19 */ @Around("execution(* *.sleep(..))") public void aroundSleep(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("I want to sleep"); joinPoint.proceed(); System.out.println("I wake"); }}
- 最后,需要在配置类中开启切面代理
/** * AOP配置类 * 需要使用@EnableAspectJAutoProxy注解开启切面代理 * * @author : zhouhao * @date : Created in 2019/3/11 19:52 */@Configuration@ComponentScans({@ComponentScan("com.zhouhao.springstudy.action"),@ComponentScan("com.zhouhao.springstudy.section")})@EnableAspectJAutoProxypublic class AopConfig {}
注:本篇文章由《Spring实战》第四章:面向切向的Spring 总结而来,由于本人非计算机专业出身,许多知识实在是理解不能,总结有相当多的遗漏,乃是我看不懂所致,更别说其中内容肯定有大量的理解错误,万望大家提出批评,我好改正。
转载地址:https://blog.csdn.net/zh328271057/article/details/88387151 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!