使用aop去自定义注解,实现用户在请求的时候记录下来,如日志功能等
首先搞清楚aop的几个概念:
AOP
即是面向切面,是Spring
的核心功能之一,主要的目的即是针对业务处理过程中的横向拓展,以达到低耦合的效果。
「切面(Aspect)」:一个关注点的模块化。以注解@Aspect
的形式放在类上方,声明一个切面。
「连接点(Joinpoint)」:在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候都可以是连接点。
「通知(Advice)」:通知增强,需要完成的工作叫做通知,就是你写的业务逻辑中需要比如事务、日志等先定义好,然后需要的地方再去用。增强包括如下五个方面:
@Before
:在切点之前执行@After
:在切点方法之后执行@AfterReturning
:切点方法返回后执行@AfterThrowing
:切点方法抛异常执行@Around
:属于环绕增强,能控制切点执行前,执行后,用这个注解后,程序抛异常,会影响@AfterThrowing
这个注解。
「切点(Pointcut)」:其实就是筛选出的连接点,匹配连接点的断言,一个类中的所有方法都是连接点,但又不全需要,会筛选出某些作为连接点做为切点。
「引入(Introduction)」:在不改变一个现有类代码的情况下,为该类添加属性和方法,可以在无需修改现有类的前提下,让它们具有新的行为和状态。其实就是把切面(也就是新方法属性:通知定义的)用到目标类中去。
「目标对象(Target Object)」:被一个或者多个切面所通知的对象。也被称做被通知(adviced
)对象。既然Spring AOP
是通过运行时代理实现的,这个对象永远是一个被代理(proxied
)对象。
「AOP代理(AOP Proxy)」:AOP
框架创建的对象,用来实现切面契约(例如通知方法执行等等)。在Spring
中,AOP
代理可以是JDK
动态代理或者CGLIB
代理。
「织入(Weaving)」:把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时(例如使用AspectJ
编译器),类加载时和运行时完成。Spring
和其他纯Java AOP
框架一样,在运行时完成织入。
Spring Boot 如何整合AOP自定义一个注解?
在实际开发中对于横向公共的逻辑需要抽取出来,这时候就需要使用AOP
,比如日志的记录、权限的验证等等,这些功能都可以用注解轻松的完成。
添加依赖starter:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
自定义一个注解:
package com.example.demo.config; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface SysLog { OperationType operationType() default OperationType.SELECT; String value() default ""; }
package com.example.demo.config; /** * @author Albert.Yang */ public enum OperationType { SELECT,SAVE,UPDATE,DELETE }
定义一个切面:
package com.example.demo.config; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; @Component @Aspect @Configuration public class SysLogAspect { @Pointcut("@annotation(com.example.demo.config.SysLog)") public void pointCut() {} @Around("pointCut()") //@Around("@annotation(sysLog)") //2种写法,一种可以不单独写@Pointcut,而以SysLog sysLog作为入参。 public Object around(ProceedingJoinPoint point) throws Throwable { long beginTime = System.currentTimeMillis(); //逻辑开始时间 System.out.println("@Around:执行目标方法之前..."); Object result = point.proceed(); //执行方法 System.out.println("@Around:执行目标方法之后..."); saveLog(point,beginTime);//todo,保存日志,自己完善 return result; } private void saveLog(ProceedingJoinPoint point, long beginTime) { System.out.println("Around-----" + beginTime + point.getSignature()); } @Before("@annotation(sysLog)") public void doBefore(JoinPoint joinPoint, SysLog sysLog) { System.out.println("doBefore-----" +joinPoint.getSignature() + sysLog.value()); } @AfterReturning(pointcut = "@annotation(sysLog)", returning = "returnValue") public void releaseResource(JoinPoint joinPoint, SysLog sysLog, Object returnValue) { System.out.println("AfterReturning-----" +joinPoint.getSignature() + sysLog.operationType().toString()); } }
测试:
以上配置完成后即可使用,只需要在需要的方法上标注@SysLog
注解即可,如下:
package com.example.demo.controller; import com.example.demo.config.SysLog; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** * @Author Tim * @Date 2020/11/20 11:40 */ @RestController public class Controller1 { @SysLog @GetMapping("/add") public String add(){ System.out.println("请求成功"); return "请求成功"; } }
package com.example.demo.controller; import com.example.demo.config.OperationType; import com.example.demo.config.SysLog; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** * @Author Tim * @Date 2020/11/20 11:40 */ @RestController public class ControlleDemo { @SysLog(operationType = OperationType.UPDATE, value = "請求2") @GetMapping("/add2") public String add(){ System.out.println("请求2"); return "请求2"; } }
注意有个执行顺序问题:
@Around是可以同时在所拦截方法的前后执行一段逻辑。
@Before是在所拦截方法执行之前执行一段逻辑。
@After 是在所拦截方法执行之后执行一段逻辑。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南