目录
文章目录
前言
一 . 场景设定和问题复现
二 . 解决技术[代理模式]
2.1 代理模式
2.2 静态代理
2.3 动态代理
三 . 面向切面编程思想(AOP)
3.1 面向切面编程思想
3.2 AOP 思想的应用场景
3.3 AOP术语名词介绍
3.3.1 横切关注点
3.3.2 通知(增强)
3.3.3 连接点
3.3.4 切入点
3.3.5 切面
3.4 Spring AOP框架介绍
四 . Spring AOP框架注解方式实现
4.1 Spring AOP底层技术组成
4.2 执行流程
4.3 AOP注解
4.4 入门案例
4.5 切入点表达式
4.6 AOP通知获取数据
4.7 切面优先级设置
总结
大家好,这篇博客给大家介绍一下spring家族中的一门框架技术aop框架
假如我们有以下四个方法
不难看出这四个方法中重复的代码有核心操作之前的代码和核心操作之后的代码
代码问题分析
1. 代码缺陷
2. 解决思路
3. 技术困难
二十三种设计模式中的一种,属于结构型模式。它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥离出来——解耦。调用目标方法时先调用代理对象的方法,减少对目标方法的调用和打扰,同时让附加功能能够集中在一起也有利于统一维护。
无代理场景:
有代理场景:
相关术语:
静态代理是在编译时就已经确定了代理类和被代理类的关系,代理类和被代理类在编译期间就已经确定下来了。在静态代理中,代理类和被代理类都需要实现同一个接口或者继承同一个父类,并且代理类中持有一个对被代理类的引用,在代理类中对被代理类的方法进行增强或者添加额外的逻辑。
简单来说,就是我们需要自己编写代理类! 不用我多说,都知道这个肯定不行,代码被我们写死了,太不优雅!
动态代理是在运行时动态生成代理类,代理类和被代理类的关系是在运行时确定的。在动态代理中,代理类不需要事先知道具体的被代理类,而是在运行时根据需要动态生成代理类。动态代理通常使用Java的反射机制来实现,在运行时生成代理类,代理类中的方法调用会被转发到真正的被代理对象上。
动态代理技术分类
不管是使用动态代理技术还是静态代理技术,我们都是需要编写代码的,程序员的工作都比较繁琐
那么有没有更好的方法来解决这个问题? 答案是肯定的,那就是AOP框架
AOP可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
AOP(面向切面编程)是一种编程范式,它通过将通用的横切关注点(如日志、事务、权限控制等)与业务逻辑分离,使得代码更加清晰、简洁、易于维护。AOP可以应用于各种场景,以下是一些常见的AOP应用场景:
1. 日志记录:在系统中记录日志是非常重要的,可以使用AOP来实现日志记录的功能,可以在方法执行前、执行后或异常抛出时记录日志。
2. 事务处理:在数据库操作中使用事务可以保证数据的一致性,可以使用AOP来实现事务处理的功能,可以在方法开始前开启事务,在方法执行完毕后提交或回滚事务。
3. 安全控制:在系统中包含某些需要安全控制的操作,如登录、修改密码、授权等,可以使用AOP来实现安全控制的功能。可以在方法执行前进行权限判断,如果用户没有权限,则抛出异常或转向到错误页面,以防止未经授权的访问。
4. 性能监控:在系统运行过程中,有时需要对某些方法的性能进行监控,以找到系统的瓶颈并进行优化。可以使用AOP来实现性能监控的功能,可以在方法执行前记录时间戳,在方法执行完毕后计算方法执行时间并输出到日志中。
5. 异常处理:系统中可能出现各种异常情况,如空指针异常、数据库连接异常等,可以使用AOP来实现异常处理的功能,在方法执行过程中,如果出现异常,则进行异常处理(如记录日志、发送邮件等)。
6. 缓存控制:在系统中有些数据可以缓存起来以提高访问速度,可以使用AOP来实现缓存控制的功能,可以在方法执行前查询缓存中是否有数据,如果有则返回,否则执行方法并将方法返回值存入缓存中。
7. 动态代理:AOP的实现方式之一是通过动态代理,可以代理某个类的所有方法,用于实现各种功能。
从每个方法中抽取出来的同一类非核心业务,在同一个项目中,我们可以使用多个横切关注点对相关放法进行不同层面的增强
核心关注点(Core Concerns)是指程序的主要功能和业务逻辑,它是程序的核心部分,与程序的业务目标直接相关。核心关注点是程序的主要目标,它决定了程序的正确性和功能完整性。通过将横切关注点与核心关注点分离,可以使得核心关注点的代码更加清晰、简洁,易于理解和维护。同时,横切关注点的代码可以在多个地方进行重复使用,提高了代码的可重用性和可维护性。通过AOP的方式管理横切关注点,可以使程序的结构更加清晰,提高代码的可维护性和可测试性
通知定义了在目标对象的方法执行前、执行后、抛出异常时等特定的时机,执行横切关注点的代码逻辑。通知可以在目标对象的方法执行前、执行后、抛出异常时等不同的时机进行执行。
每一个横切关注点上要做的事情都需要写一个方法来实现,这样的方法就叫通知方法。
AOP中定义了几种常见的通知类型:
前置通知(Before Advice):在目标对象的方法执行前执行的通知。可以在前置通知中进行一些准备工作,例如参数校验、权限检查等。
后置通知(After Advice):在目标对象的方法执行后执行的通知。可以在后置通知中进行一些清理工作,例如释放资源、记录日志等。
返回通知(After Returning Advice):在目标对象的方法执行后并且成功返回结果时执行的通知。可以在返回通知中获取方法的返回值,并进行一些后续处理。
异常通知(After Throwing Advice):在目标对象的方法抛出异常时执行的通知。可以在异常通知中捕获并处理异常,或者进行一些异常处理的逻辑。
环绕通知(Around Advice):在目标对象的方法执行前后都执行的通知。环绕通知可以完全控制目标对象方法的执行过程,可以在方法执行前进行一些预处理,然后决定是否继续执行目标方法,最后在方法执行后进行一些后续处理。
连接点(Join Point)是AOP中的一个概念,表示在程序执行过程中可以插入通知的特定点。也可以说,连接点是程序中可以应用通知的位置。
在一个应用程序中,连接点可以是方法的执行、方法的调用、异常的抛出、字段的访问等等。AOP框架通过拦截连接点来应用通知,从而实现对横切关注点的控制和管理。
定位连接点的方式,或者可以理解成被选中的连接点!
是一个表达式,比如execution(* com.spring.service.impl.*.*(..))。符合条件的每个方法都是一个具体的连接点。
切入点和通知的结合,是一个类
1. AOP一种区别于OOP的编程思维,用来完善和解决OOP的非核心代码冗余和不方便统一维护问题!
2. 代理技术(动态代理|静态代理)是实现AOP思维编程的具体技术,但是自己使用动态代理实现代码比较繁琐!
3. Spring AOP框架,基于AOP编程思维,封装动态代理技术,简化动态代理技术实现的框架!SpringAOP内部帮助我们实现动态代理,我们只需写少量的配置,指定生效范围即可,即可完成面向切面思维编程的实现!
调用目标方法,底层会根据切入点和通知生成代理对象如果有接口就会生成JDK动态代理,反之则生成cglib动态代理
常见的AOP注解包括:
@Aspect:定义切面类,用于描述切入点和通知。
@Pointcut:定义切入点,用于描述需要拦截的连接点。
@Before:定义前置通知,表示在目标对象方法执行前执行的通知。
@After:定义后置通知,表示在目标对象方法执行后执行的通知。
@AfterReturning:定义返回通知,表示在目标对象方法执行后并且成功返回结果时执行的通知。
@AfterThrowing:定义异常通知,表示在目标对象方法抛出异常时执行的通知。
@Around:定义环绕通知,表示在目标对象方法执行前后都执行的通知。
在AOP中,通知可以获取连接点(即被拦截的方法或者类)的相关数据。通知可以通过参数来接收这些数据,常见的参数类型包括:
JoinPoint:连接点对象,可以获取连接点的相关信息,如方法名称、参数等。可以通过JoinPoint的方法来获取这些信息,例如getSignature()获取方法签名,getArgs()获取方法参数等。
ProceedingJoinPoint:继承自JoinPoint,用于环绕通知中,可以控制目标方法的执行。可以调用ProceedingJoinPoint的proceed()方法来执行目标方法,还可以通过getArgs()获取方法参数等。
常用的JoinPoint和ProceedingJoinPoint的方法包括:
getSignature():获取连接点的方法签名对象,返回一个MethodSignature对象。可以通过MethodSignature对象获取方法的详细信息,如方法名称、返回类型、参数类型等。
getArgs():获取连接点的方法参数,返回一个Object数组。可以通过数组索引获取具体的方法参数。
getTarget():获取连接点的目标对象,即被拦截的对象。
getThis():获取连接点的当前对象,即代理对象。
对于ProceedingJoinPoint,除了继承了JoinPoint的方法外,还有以下常用方法:
proceed():执行目标方法。在环绕通知中,可以通过调用proceed()方法来控制目标方法的执行。
proceed(Object[] args):执行目标方法,并传入指定的参数。在环绕通知中,可以通过传入参数来控制目标方法的执行。
在AOP中,切面的优先级可以通过设置切面的order属性来进行配置。order属性是一个整数值,数值越小的切面优先级越高。
可以使用@Order注解来设置切面的优先级。例如:
@Aspect
@Order(1)
public class FirstAspect {
// 切面逻辑代码
}
@Aspect
@Order(2)
public class SecondAspect {
// 切面逻辑代码
}
在上面的例子中,FirstAspect的优先级为1,SecondAspect的优先级为2。因此,FirstAspect的切面逻辑会先于SecondAspect执行。
实际意义:
实际开发时,如果有多个切面嵌套的情况,要慎重考虑。例如:如果事务切面优先级高,那么在缓存中命中数据的情况下,事务切面的操作都浪费了。
此时应该将缓存切面的优先级提高,在事务操作之前先检查缓存中是否存在目标数据。
这篇博客主要讲解了Spring AOP相关的内容,我们下一篇博客见