news 2026/1/12 13:38:34

【Java基础】AOP与注解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Java基础】AOP与注解

一、注解

1、注解定义

注解本质上是一个实现了annotation的特殊接口,其具体实现类是Java运行时生成的动态代理类。通过反射获取注解时,返回的是Java运行时生成的动态代理对象。

// 定义注解@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public@interfaceMyAnnotation{Stringvalue()default"default";intcount()default0;}// 编译后:注解本质是一个接口,继承自AnnotationpublicinterfaceMyAnnotationextendsjava.lang.annotation.Annotation{Stringvalue();intcount();}

2、注解作用域和作用范围

2.1 作用域 (@Retention)

根据作用域,可以分为如下三类:

源码级:只在源码中存在,编译后会不会保留,(@Retention(RetentionPolicy.SOURCE))。

类文件级别注解:保留在.class文件中,但运行时不可见,(@Retention(RetentionPolicy.CLASS))。

运行时注解:保留在.class文件中,只有这个作用域可以通过反射在运行时访问(@Retention(RetentionPolicy.RUNTIME))。

2.2 作用范围

作用范围有方法、属性、类

二、AOP

1. 定义切面类

切面类需使用@Aspect标识,并纳入 Spring 容器管理:

@Aspect @Component @Slf4j public class CacheAspect { }

2. 定义切点(Pointcut)

切点用于描述哪些方法/类/连接点需要被拦截。


2.1 常见 Pointcut 表达式

2.1.1@annotation

匹配方法上标记了指定注解的连接点:

// 匹配所有带 @CacheAble 注解的方法 @Pointcut("@annotation(org.example.aop.CacheAble)") public void cacheAbleCut(){}

技巧
可在 Advice 入参中直接绑定注解对象:

@Around("cacheAbleCut() && @annotation(cacheAble)") public Object around(ProceedingJoinPoint jp, CacheAble cacheAble) { ... }

这样可以直接获取注解属性值,无需手动反射。


2.1.2execution

根据方法签名匹配:

// 匹配所有公共方法 @Pointcut("execution(public * *(..))") public void anyPublicMethod() {} // 匹配所有以 set 开头的方法 @Pointcut("execution(* set*(..))") public void anySetMethod() {} // 匹配 com.example.service 包下所有类的所有方法 @Pointcut("execution(* com.example.service.*.*(..))") public void serviceMethods() {} // 匹配 UserDao 接口的所有实现方法 @Pointcut("execution(* com.example.dao.UserDao.*(..))") public void userDaoMethods() {} // 匹配返回值类型为 User 的方法 @Pointcut("execution(com.example.model.User *(..))") public void methodsReturningUser() {}

2.1.3@within@target

匹配类级别注解:

// 匹配带 @Service 注解类中的所有方法 @Pointcut("@within(org.springframework.stereotype.Service)") public void serviceAnnotationMethods() {} // 匹配目标对象带 @Repository 注解的连接点 @Pointcut("@target(org.springframework.stereotype.Repository)") public void repositoryAnnotationMethods() {}

区别:

  • @within→ 检查声明类是否有注解
  • @target→ 检查运行时目标类是否有注解(Spring 代理场景可能不同)

2.1.4 组合切点

支持&&(与)、||(或)、!(非):

// 在 service 包中且为公共方法 @Pointcut("serviceMethods() && anyPublicMethod()") public void servicePublicMethods() {} // service 包 或 dao 包方法 @Pointcut("serviceMethods() || daoMethods()") public void serviceOrDaoMethods() {} // 非事务性 service 方法 @Pointcut("serviceMethods() && !transactionalMethods()") public void serviceNonTransactionalMethods() {}

2.2 其他常用匹配方式

2.2.1args

绑定方法参数:

@Pointcut("execution(* com.example..*(..)) && args(userId,..)") public void methodWithUserIdArg(String userId) {}

Advice 可直接接收参数:

@Before("methodWithUserIdArg(userId)") public void logUser(String userId) { ... }
2.2.2this/target
  • this(proxy):绑定代理对象
  • target(obj):绑定真实目标对象
@Around("this(proxy) && target(service)") public Object around(ProceedingJoinPoint jp, Object proxy, MyService service) { ... }

3.定义切面

在 AOP 中,切面逻辑主要通过以下几种Advice来实现:

通知类型注解作用时机
前置通知@Before目标方法执行前
后置通知@After目标方法执行后(无论是否异常)
返回通知@AfterReturning目标方法成功返回后
异常通知@AfterThrowing目标方法抛出异常后
环绕通知@Around在目标方法前后都可执行,并可决定是否执行目标方法

​ 其中ProceedingJoinPoint是JoinPoint的子类,由于Around需要执行源方法,ProceedingJoinPoint针对@Around环绕通知提供了proceed()来调用源方法。

3.1 前置通知 @Before

@Before("cacheAbleCut()")publicvoidbeforeMethod(JoinPointjoinPoint){log.info("执行前置通知: 方法 = {}, 参数 = {}",joinPoint.getSignature().getName(),Arrays.toString(joinPoint.getArgs()));}

3.2 后置通知 @After

@After("cacheAbleCut()")publicvoidafterMethod(JoinPointjoinPoint){log.info("执行后置通知: 方法 = {}",joinPoint.getSignature().getName());}

3.3 返回通知 @AfterReturning

@AfterReturning(pointcut="cacheAbleCut()",returning="result")publicvoidafterReturning(JoinPointjoinPoint,Objectresult){log.info("方法 {} 成功返回,返回值 = {}",joinPoint.getSignature().getName(),result);}

3.4 异常通知 @AfterThrowing

@AfterThrowing(pointcut="cacheAbleCut()",throwing="ex")publicvoidafterThrowing(JoinPointjoinPoint,Throwableex){log.error("方法 {} 抛出异常: {}",joinPoint.getSignature().getName(),ex.getMessage(),ex);}

3.5 环绕通知 @Around

@Around是功能最强的通知,可以控制方法执行与否,并在执行前后做处理:

@Around("cacheAbleCut()")publicObjectaround(ProceedingJoinPointpjp)throwsThrowable{log.info("环绕通知开始 - 方法: {}",pjp.getSignature().getName());longstartTime=System.currentTimeMillis();// 执行目标方法Objectresult=pjp.proceed();longendTime=System.currentTimeMillis();log.info("环绕通知结束 - 耗时: {}ms",(endTime-startTime));// 返回的即最终方法执行后返回的结果,可以自定义随意返回returnresult;}
高级用法:绑定注解实例

如果希望直接在环绕通知的方法参数中获取到目标方法上的注解对象,可以这样写:

@Around("aopPoint() && @annotation(rateLimiterAccessInterceptor)")publicObjectdoRouter(ProceedingJoinPointjp,RateLimiterAccessInterceptorrateLimiterAccessInterceptor)throwsThrowable{log.info("限流检查: key={}, permits={}",rateLimiterAccessInterceptor.key(),rateLimiterAccessInterceptor.permits());returnjp.proceed();}

说明

  • && @annotation(rateLimiterAccessInterceptor)
    限定切点只匹配带有该注解的方法,并且将注解实例注入到参数中。
  • 方法参数RateLimiterAccessInterceptor rateLimiterAccessInterceptor
    直接就是方法上真实存在的注解对象,不需要手动反射getMethod()getAnnotation()

示例目标方法:

@RateLimiterAccessInterceptor(key="userLogin",permits=5)publicvoidlogin(Stringusername){// 登录逻辑}

执行时 AOP 会自动把@RateLimiterAccessInterceptor注解实例注入到doRouter()的第二个参数中。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2025/12/26 19:18:52

【无人机三维路径规划】基于A_Satr结合天牛算法BAS野外环境下考虑模态切换点优化的3D路径规划附Matlab代码和报告

✅作者简介:热爱科研的Matlab仿真开发者,擅长数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室🍊个人信条:格物致知,完整Matlab代码获取及仿真…

作者头像 李华
网站建设 2025/12/30 9:21:30

告别无效调用:高效实现 AI Agent 的 Function 交互设计

做 Agent 之前,彻底搞懂 AI 的 Function Calling / Tools 上篇(链接如上)我们解决了一个问题:让 AI 按你说的格式,老老实实返回 JSON。 现在这件事你已经会干了: 用户说:「我想查上海的天气」AI 乖乖返回:{…

作者头像 李华
网站建设 2025/12/31 13:24:30

智能喂食器:云计算赋能宠物科技

在家庭自动化与物联网技术爆发的当下,智能宠物喂食器已从 “小众刚需” 升级为 “科技生活标配”。这款集成了硬件控制、传感器感知、远程通信与云端协同的智能设备,本质是云计算与物联网深度融合的典型落地场景—— 它不仅解决了宠物主人 “远程喂饭” …

作者头像 李华
网站建设 2026/1/4 9:48:04

5MW风电永磁直驱发电机-1200V直流并网Simulink仿真模型

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

作者头像 李华