Spring AOP 注解及使用示例
什么是 AOP
AOP是Spring框架面向切面的编程思想,AOP采用一种称为“横切”的技术,将涉及多业务流程的通用功能抽取并单独封装,形成独立的切面,在合适的时机将这些切面横向切入到业务流程指定的位置中。
切面:相当于应用对象间的横切点,我们可以将其单独抽象为单独的模块
AOP 术语
AOP 领域中的特性术语:
- 通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。
- 连接点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。
- 切点(PointCut): 可以插入增强处理的连接点。
- 切面(Aspect): 切面是通知和切点的结合。
- 引入(Introduction):引入允许我们向现有的类添加新的方法或者属性。
- 织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程就是织入
通过注解声明切点指示器
AspectJ指示器 | 描述 |
---|---|
arg() | 限制连接点匹配参数为指定类型的执行方法 |
@args() | 限制连接点匹配参数由指定注解标注的执行方法 |
execution() | 用于匹配是连接点的执行方法 |
this() | 限制连接点匹配AOP 代理的Bean 引用为指定类型的类 |
target() | 限制连接点匹配目标对象为指定类型的类 |
@target() | 限制连接点匹配特定的执行对象.这些对象对应的类要具备指定类型的注解 |
within() | 限制连接点匹配指定的类型 |
@within() | 限制连接点匹配指定注解所标注的类型(当使用Spring AOP时,方法定义在由指定的注解所标注的类里) |
@annotation() | 限制匹配带有指定注解连接点 |
通过注解声明 5 种通知类型
注解 | 描述 |
---|---|
@Before | 通知方法会在目标方法调用之前执行 |
@After | 通知方法会在目标方法返回或异常后调用 |
@AfterReturning | 通知方法会在目标方法返回后调用 |
@AfterThrowing | 通知方法会在目标方法抛出异常后调用 |
@Around | 通知方法会将目标方法封装起来 |
@Pointcut //定义切点位置
使用:
@Pointcut("execution(* com.springinaction.springidol.Instrument.play(..))")
AOP 示例
下列代码基于SpringBoot 工程,部分代码已省略,请自行创建
1.创建AopController.java
package com.soft.controller; import com.google.gson.Gson; import com.soft.service.IAopService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** * Aop测试 * @description: AopController * @author: suphowe * @create: 2019-12-25 14:46 **/ @RestController @RequestMapping("/aop") @Api(value = "Aop切面编程测试") public class AopController { @Autowired private IAopService aopService; @ResponseBody @RequestMapping(value = "/method_one", method = RequestMethod.POST) @ApiOperation(value = "方法1", notes = "") @ApiImplicitParams({ @ApiImplicitParam(paramType = "query", name = "msg", value = "msg", required = false, dataType = "String") }) public String method_one(String msg){ String result = aopService.method_one(msg); return result; } }
2.创建 IAopService.java 和 AopServiceImpl.java
package com.soft.service; public interface IAopService { String method_one(String msg); }
package com.soft.service.impl;
import com.soft.service.IAopService;
import org.springframework.stereotype.Service;
@Service
public class AopServiceImpl implements IAopService {
@Override
public String method_one(String msg){
System.out.println("==>接收信息:" + msg);
return "return method_one";
}
}
3.创建 SysAspect.java
package com.soft.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class SysAspect {
/**
* 声明切点
*/
@Pointcut("execution(* com.soft.service.*.*(..))")
public void point(){}
/**
* 通知方法会在目标方法调用之前执行
*/
@Before("point())")
public void before() {
System.out.println("before ==> 通知方法会在目标方法调用之前执行");
}
/**
* 通知方法会在目标方法返回或异常后调用
*/
@After("point()")
public void after() {
System.out.println("After ==> 通知方法会在目标方法返回或异常后调用");
}
/**
* 通知方法会在目标方法返回后调用
*/
@AfterReturning("point()")
public void afterReturing() {
System.out.println("AfterReturning ==> 通知方法会在目标方法返回后调用");
}
/**
* 通知方法会在目标方法抛出异常后调用
*/
@AfterThrowing("point()")
public void afterThrowing() {
System.out.println("AfterThrowing ==> 通知方法会在目标方法抛出异常后调用");
}
/**
* 通知方法会将目标方法封装起来
* @param proceeding
*/
@Around("point()")
public Object around(ProceedingJoinPoint proceeding) throws Exception{
//和JoinPoint一样,ProceedingJoinPoint也可以获取
//连接点方法的实参
Object[] args=proceeding.getArgs();
//连接点方法的方法名
String methodName=proceeding.getSignature().getName();
//连接点方法所在的对象
Object targetObj=proceeding.getTarget();
String targetClassName=targetObj.getClass().getName();
Object result=null;
try {
System.out.println("前置通知==>参数:" + args[0]);
System.out.println("前置通知==>执行方法:" + methodName);
//执行连接点的方法 获取返回值
result=proceeding.proceed(args);
System.out.println("返回通知==>返回结果:" + result);
}catch (Throwable e) {
System.out.println("异常通知==>" + e);
}finally {
System.out.println("最终通知==>执行结束");
}
return result;
}
}
4.启动工程,通过Swagger-ui.html访问
swagger访问结果
控制台日志输出结果
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)