Spring学习笔记——(4)AOP(一)
程序员文章站
2022-06-05 09:13:54
...
一、简介
- AOP(Aspect-Oriented Programming, 面向切面编程): 是一种新的方法论, 是对传统 OOP(Object-Oriented Programming, 面向对象编程) 的补充.
- AOP 的主要编程对象是切面(aspect), 而切面模块化横切关注点.
- 在应用 AOP 编程时, 仍然需要定义公共功能, 但可以明确的定义这个功能在哪里, 以什么方式应用, 并且不必修改受影响的类. 这样一来横切关注点就被模块化到特殊的对象(切面)里.
- AOP 的好处:每个事物逻辑位于一个位置, 代码不分散, 便于维护和升级。 业务模块更简洁, 只包含核心业务代码.
二、AOP示例
三、AOP术语
1、 切面(Aspect)
- 切面由切点和增强(引介)组成,它既包括了横切逻辑的定义,也包括了连接点的定义,Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。
- 横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象,如上述的验证、日志
2、通知、增强(Advice)
- 切面必须要完成的工作,切面中的具体的功能,如上述的前置日志、后置日志和验证参数。
- 是织入到目标类连接点上的一段程序代码。比如:添加日志、管理事务。
3、目标(Target)
- 增强逻辑的织入目标类。如果没有AOP,目标业务类需要自己实现所有逻辑,而在AOP的帮助下,目标业务类只实现那些非横切逻辑的程序逻辑,而性能监视和事务管理等这些横切逻辑则可以使用AOP动态织入到特定的连接点上。
- 被通知的对象,如上述的add方法,sub方法等,被加入功能的对象(java中一切皆对象)
4、代理(Proxy)
- 一个类被AOP织入增强后,就产出了一个结果类,它是融合了原类和增强逻辑的代理类。根据不同的代理方式,代理类既可能是和原类具有相同接口的类,也可能就是原类的子类,所以我们可以采用调用原类相同的方式调用代理类。
- 向目标对象应用通知之后创建的对象
5、连接点(Joinpoint)
- 程序执行的某个特定位置:如类开始初始化前、类初始化后、类某个方法调用前、调用后、方法抛出异常后。一个类或一段程序代码拥有一些具有边界性质的特定点,这些点中的特定点就称为“连接点”。Spring仅支持方法的连接点,即仅能在方法调用前、方法调用后、方法抛出异常时以及方法调用前后这些程序执行点织入增强。连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位。例如 ArithmethicCalculator#add() 方法执行前的连接点,执行点为 ArithmethicCalculator#add(); 方位为该方法执行前的位置
6、切点(pointcut)
- 每个类都拥有多个连接点:例如 ArithmethicCalculator 的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP 通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。
- Spring AOP的规则解析引擎负责切点所设定的查询条件,找到对应的连接点。其实确切地说,不能称之为查询连接点,因为连接点是方法执行前、执行后等包括方位信息的具体程序执行点,而切点只定位到某个方法上,所以如果希望定位到具体连接点上,还需要提供方位信息。
7、织入(Weaving)
织入是将增强添加对目标类具体连接点上的过程。AOP像一台织布机,将目标类、增强或引介通过AOP这台织布机天衣无缝地编织到一起。根据不同的实现技术,AOP有三种织入的方式:
- a、编译期织入,这要求使用特殊的Java编译器。
- b、类装载期织入,这要求使用特殊的类装载器。
- c、动态代理织入,在运行期为目标类添加增强生成子类的方式。
- Spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
8、引介(Introduction)
引介是一种特殊的增强,它为类添加一些属性和方法。这样,即使一个业务类原本没有实现某个接口,通过AOP的引介功能,我们可以动态地为该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。
四、AspectJ
1、AspectJ介绍
- AspectJ:Java 社区里最完整最流行的 AOP 框架.
- 在 Spring2.0 以上版本中, 可以使用基于 AspectJ 注解或基于 XML 配置的 AOP
2、在Spring中配置AspectJ
(1)引入jar包
<!-- https://mvnrepository.com/artifact/aspectj/aspectjrt -->
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.5.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8</version>
</dependency>
(2)Spring配置自动扫描包,和使 AspectJ 的注解起作用
当 Spring IOC 容器侦测到 Bean 配置文件中的 <aop:aspectj-autoproxy> 元素时, 会自动为与 AspectJ 切面匹配的 Bean 创建代理.
<!-- 自动扫描的包 -->
<context:component-scan base-package="com.shaohe.spring.aop.helloword"></context:component-scan>
<!-- 使 AspectJ 的注解起作用 :自动为匹配的类生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
(3)创建切面
@Aspect // 声明类为一个切面
@Component // 类交由IOC容器管理
public class LoggingAspect {
/**
* 前置通知,在目标方法开始之前执行。<br>
* aaa@qq.com 标记的方法的方法体.<br>
* aaa@qq.com 里面的是切入点表达式<br>
* 3.JoinPoint 类型的参数:从中可以访问到方法的签名和方法的参数.
*/
@Before("execution(public int com.shaohe.spring.aop.helloword.ArithmeticCalculator.*(int, int))")
public void beforeMethod(JoinPoint joinPoint) {
// 方法名称
String methodName = joinPoint.getSignature().getName();
// 参数数组
Object[] args = joinPoint.getArgs();
System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
}
/**
* 后置通知:@After,在方法执行之后执行的代码.
*/
@After("execution(* com.shaohe.spring.aop.helloword.*.*(..))")
public void afterMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " ends");
}
}
3、总结:
- 要在 Spring 中声明 AspectJ 切面, 只需要在 IOC 容器中将切面声明为 Bean 实例. 当在 Spring IOC 容器中初始化 AspectJ 切面之后, Spring IOC 容器就会为那些与 AspectJ 切面相匹配的 Bean 创建代理.
- 在 AspectJ 注解中, 切面只是一个带有 @Aspect 注解的 Java 类.
- 通知是标注有某种注解的简单的 Java 方法.
- AspectJ 支持 5 种类型的通知注解:
@Before: 前置通知, 在方法执行之前执行
@After: 后置通知, 在方法执行之后执行
@AfterRunning: 返回通知, 在方法返回结果之后执行
@AfterThrowing: 异常通知, 在方法抛出异常之后
@Around: 环绕通知, 围绕着方法执行