发布时间:2022-08-19 11:30
AOP(Aspect Oriented Programming),即面向切面编程。 (OOP:Object 面向对象编程)
比如去银行取款和查询余额
有了AOP,你写代码时不需要把这个验证用户步骤写进去,即完全不考虑验证用户。只写取款和显示余额的业务代码。而在另一个地方,写好验证用户的代码。这个验证用户的代码就是切面代码,以后在执行取款和显示余额的时候,利用代理模式。将验证用户的功能在执行取款和显示余额前调用。
代码在Spring容器中执行的时候,通过配置告诉Spring你要把这段代码加到哪几个地方,Spring就会在执行正常业务流程的时候帮你把验证代码和取款代码织入到一起。
AOP真正目的是:你写代码的时候,只需考虑主流程,而不用考虑那些不重要的,但又必须要写的其它相同的代码,这些其它的相同代码所在的类就是切面类。
面向切面编程的核心就是代理模式。
JoinPoint(连接点):在程序执行过程中的某个阶段点,连接点就是指主业务方法的调用,它是客观存在的。
Pointcut(切入点):切入点指的是类或者方法名,满足某一规则的类或方法都是切入点,通过切入点表达式来制定规则。
Advice(通知):切入点处所要执行的程序代码,即切面类中要执行的公共方法。通知的类型有: 前置通知,后置通知,异常通知,最终通知,环绕通知。
Target(目标对象):被代理的对象。比如动态代理案例中的明星,房东。
Weaving(织入):织入指的是把新增的功能用于目标对象,创建代理对象的过程。
Proxy(代理):一个类被AOP织入增强后产生的结果类,即代理类。比如动态代理案例中的经纪人或中介
Aspect(切面):切面指的是切入点(规则)和通知(织入方法)的类 = 切入点+通知
Spring在AOP编程中使用代理的方式
目标对象有接口:使用JDK代理
目标对象没有接口:使用CGLIB代理
在Spring的AOP编程中,代理对象则由Spring创建,不用自己写了。
我们要做的就是配置AOP
当向数据库中保存账户的时候,使用日志记录下这次保存操作
开发业务类:添加账户
开发切面类:记录日志
使用AOP将业务类与切面类织入到一起,实现需求的功能
AccountService业务接口有void save() 添加账户的方法
AccountServiceImpl实现业务接口类,输出"保存账户"
创建LogAspect切面类,编写通知方法void printLog(),输出:"执行添加操作"
配置文件applicationContext.xml
导入spring-context
导入开源的面向切面编程的组件: aspectjweaver
导入JUnit5
4.0.0
com.it
AOP
1.0-SNAPSHOT
org.springframework
spring-context
5.2.0.RELEASE
org.aspectj
aspectjweaver
1.8.13
org.springframework
spring-test
5.2.0.RELEASE
test
org.junit.jupiter
junit-jupiter
5.5.2
test
package com.it.service;
/**
* 账户业务接口
*/
public interface AccountService {
/**
* 保存账户
*/
void save();
}
package com.it.service.impl;
import com.it.service.AccountService;
public class AccountServiceImpl implements AccountService {
/**
* 保存账户
*/
@Override
public void save() {
System.out.println("保存账户");
}
}
package com.it.utils;
import java.sql.Timestamp;
/**
* 记录日志功能的类:切面类 = 切入点(规则)+通知(方法)
*/
public class LogAspect {
/**
* 记录日志
*/
public void printLog() {
System.out.println(new Timestamp(System.currentTimeMillis()) + " 记录日志");
}
}
XML中关于AOP的配置
配置日志记录类,这是切面类:LogAspect
配置正常的业务类:AccountServiceImpl
AOP配置 execution(public void com.it.service.impl.AccountServiceImpl.save())
注:在导入aop的命名空间,idea可以自动导入
/**
* 测试类
*/
@ExtendWith(SpringExtension.class) //指定第三方运行类
@ContextConfiguration("classpath:applicationContext.xml")
public class TestAccount {
@Autowired
private AccountService accountService;
@Test
public void testSave() {
System.out.println("业务对象类型:" + accountService.getClass());
accountService.save();
}
}
业务对象类型:class com.sun.proxy.$Proxy17
2021-01-11 09:22:10.164 记录日志
保存账户
切入点表达式的作用:一组规则,指定哪些类和方法要被切入
切入点函数 | 作用 |
---|---|
execution | 细粒度函数,精确到方法 |
within | 粗粒度,只能精确到类 |
bean | 粗粒度,精确到类,从容器中通过id获取对象 |
?表示出现0次或1次
() 没有参数
(*) 1个或多个参数
(..) 0个或多个参数.. 表示当前包和子包
前置通知:在主业务方法前执行
后置通知:在主业务方法后执行
异常通知:在主业务方法抛出异常的时候执行
最终通知:无论主业务方法是否出现异常,都会执行。注:如果配置在后置通知的前面,会先执行最终通知
环绕通知:相当于上面所有通知的组合