AOP面向切面编程
AOP简介
AOP(Aspect Oriented Progrmming)面向切面编程,通过预编译方式和运行期间动态代理实现功能的统一维护的一种技术。
Aop的作用
- 在程序运行期间,不修改代码的同时为程序增强功能。
- 将必不可少的公共功能·做成切面,随程序运行切入到代码中运行。
- 编写业务时只关注于核心功能,减轻编码负担,更专注业务。
AOP的术语
- 连接点(join point):程序执行的某个特定位置,例如一个拥有两个方法的类,这两个方法都是连接点。
- 切入点(Pointcut);每一个程序类都拥有多个连接点,AOP通过切入点定位特定的连接点。连接点相当于数据库中的记录,切入点就是查询条件,一个切点可以匹配多个连接点。切点定义了在哪里做。
- 通知(Advice):通知是织入到目标连接点上的一段代码。在Spring中,通知除了用于描述一段代码外,还拥有另一个和连接点相关的信息,即执行点方位。结合执行点方位信息和切点信息,我们就可以找到特定的连接点。通知定义了需要做什么,在某个连接点的什么时候做。
- Spring切面可以应用五种类型的通知
- 前置通知(Before):在目标方法被调用之前调用通知功能
- 后置通知(After):在目标方法被调用之后调用通知功能
- 返回通知(After-returning):在方法成功执行之后调用通知
- 异常通知(After-throwing):在目标方法抛出异常后调用通知
- 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。环绕通知可以修改入参值
- 切面(Aspect):切面由切点和通知组成,它既包括了横切逻辑的定义,也包括连接点的定义。Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。
- 切面是切点的通知的结合,切面知道所有它需要做的事:何时做,何处做,做什么
- 目标对象( Target object):即需要增强的对象,通知逻辑的织入目标类。如果没有AOP,目标业务类需要自己实现所有逻辑,而在AOP的帮助下,目标业务类只实现那些非横切逻辑的程序逻辑,而性能监视和事务管理等这些横切逻辑则可以使用AOP动态织入到特定的连接点上
- 织入(Weaving):织入是将通知添加对目标类具体连接点上的过程。AOP像一台织布机,将目标类、通知或引介通过AOP这台织布机天衣无缝地编织到一起。根据不同的实现技术,AOP有三种织入的方式:
- 编译期织入,这要求使用特殊的Java编译器
- 类装载期织入,这要求使用特殊的类装载器
- 动态代理织入,在运行期为目标类添加通知生成子类的方式
- 织入是把切面应用到目标对象来创建新的代理对象的过程,Spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入
- 代理(Proxy):一个类被AOP织入通知后,就产出了一个结果类,它是融合了原类和通知逻辑的代理类。根据不同的代理方式,代理类既可能是和原类具有相同接口的类,也可能就是原类的子类,所以我们可以采用调用原类相同的方式调用代理类
使用注解实现AOP
常用注解:
@Aspect 切面,配置到切面类上
@PointCut("表达式") 配置切入点,加在方法上
@Before 配置前置通知方法
@After 配置后置通知方法
@Around 配置环绕通知方法
@AfterReturning 配置后置返回值通知方法
@AfterThrowing 配置后置抛出异常通知方法
例子:用AOP完成对service层所有方法的日志跟踪,日志能输出当前方法名,传入的参数值,执行时间(使用环绕)
导包:
- spring-context
- spring-aop
- spring-test
- aspectjrt
- aspectjweaver
- junit
配置类:
SpringConfig.java
//配置类注解 @Configurable //包扫描注解 @ComponentScan(basePackages = "cn.blb.aop") //开机aop注解 @EnableAspectJAutoProxy public class SpringConfig { }
用户服务接口:
UserService.java
//用户服务接口 public interface UserService { //方法1 public String updateMoney(String name,double money); //方法2 public String updateMoney2(String name); }
实现用户服务接口:
UserServiceImpl.java
//spring服务层注解 @Service //实现用户服务接口 public class UserServiceImpl implements UserService { @Override public String updateMoney(String name, double money) { System.out.println("我向"+name+"转账"+money); return name; } @Override public String updateMoney2(String name) { System.out.println("我向"+name+"转账"); return name; } }
增加的功能:
UserLogService.java
//配置切面 @Aspect @Component public class UserLogService { //配置切点 @Pointcut("execution(* cn.blb.aop..*.*(..))") public void pointcut() { } //使用通知环绕 @Around("pointcut()") public void getLog(ProceedingJoinPoint proceedingJoinPoint) { //获取方法名 String name = proceedingJoinPoint.getSignature().getName(); System.out.println("方法名:"+name); //获取传入的参数 Object[] args = proceedingJoinPoint.getArgs(); System.out.println("传入的参数有:"); for (Object a : args) { System.out.println(a); } //获取当前时间 Date data = new Date(); //设置时间格式 SimpleDateFormat form = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String time = form.format(data); System.out.println("执行时间" + time); try { //调用增强的方法 proceedingJoinPoint.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } } }
值得一提的是:环绕通知可以修改入参值
调用被增强的方法时,调用另一个processd(new Object[ ]{参数值})
测试类:
Test.java
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfig.class) public class testUserService { //Spring的自动装配注解 @Autowired private UserService userService; @Test public void testService(){ userService.updateMoney("张山",2000D); userService.updateMoney2("李四"); } }
运行结果:
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· spring官宣接入deepseek,真的太香了~