AspectJ的拓展学习--织入顺序和通知参数指定
前言:
其实spring的aop非常的强大, 因此研究一下AspectJ还是有必要, 而不是仅仅停留在初级的阶段.
比如spring的事务是基于aop来实现的, 如果不能深入的研究, 可能很多知识点, 只知其然而不知其所以然.
本文将简单地讲述如何指定AspectJ的织入顺序, 以及如何指定通知参数.
AspectJ的博文:
以下博文是之前实战中记录的.
1. 利用Aspectj实现Oval的自动参数校验
2. 类Shiro权限校验框架的设计和实现
以下博文是本文参考的文章(强烈推荐):
1. AspectJ切入点语法详解
织入顺序:
如果同一个函数调用, 涉及多个AOP的织入, 那么这些AOP的顺序该如何定义和指定? 为了解决这个问题, AspectJ引入了Order, 它约定了order数值越小, 优先级越高(越早被调用).
AspectJ类指定顺序的方式有两种.
1. 引入注解@Order
1 2 3 4 5 6 7 8 9 10 11 12 | import org.springframework.core.annotation.Order; @Aspect @Component @Order ( 1 ) public class MyAdvice1 { @Pointcut ( "execution(* com.springapp.mvc.controller.*.*(..))" ) public void pointCut() { } } |
2. 实现Ordered接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import org.springframework.core.Ordered; @Aspect @Component public class MyAdvice2 implements Ordered { @Pointcut ( "execution(* com.springapp.mvc.controller.*.*(..))" ) public void pointCut() { } @Override public int getOrder() { return 2 ; } } |
无论是那种, 其遵守的标准是一定的.
总的来说, 其顺序规则如下:
1. 在同一切面类内, 按照切入点的定义顺序来织入
2. 在不同的切面类内, 都实现了Ordered接口, 按切入点的Order数值从小到达织入.
3. 在不同的切面类内, 存在没实现Ordered接口的类, 则切入点的顺序不确定.
通知参数指定:
通知参数的指定, 一定程度上是为方便编程, 提升了开发效率.
我之前对Aspectj了解没那么深入的时候, 一直用吃力不讨好的方式在开发.
比如之前写的权限校验小框架, 其核心代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | @Target ({ElementType.TYPE, ElementType.METHOD}) @Retention (RetentionPolicy.RUNTIME) public @interface MyRequiresRoles { String[] value(); MyLogic logic() default MyLogic.AND; } @Aspect @Component public class MyShiroAdvice { /** * 定义切点, 用于角色的校验 */ @Pointcut ( "@annotation(com.springapp.mvc.myshiro.MyRequiresRoles)" ) public void checkRoles() { } @Before ( "checkRoles()" ) public void doCheckRole(JoinPoint jp) throws Exception { // *) 从JointPoint变量中提取对应的注解 MyRequiresRoles mrp = extractAnnotation( (MethodInvocationProceedingJoinPoint)jp, MyRequiresRoles. class ); try { // 获取注解设置的值(角色集合, 逻辑操作), 进行评估判断 if ( !MyShiroHelper.validateRoles(mrp.value(), mrp.logic()) ) { throw new Exception( "access disallowed" ); } } catch (Exception e) { throw new Exception( "invalid state" ); } } // *) 获取注解信息 private static <T extends Annotation> T extractAnnotation( MethodInvocationProceedingJoinPoint mp, Class<T> clazz) throws Exception { Field proxy = mp.getClass().getDeclaredField( "methodInvocation" ); proxy.setAccessible( true ); ReflectiveMethodInvocation rmi = (ReflectiveMethodInvocation) proxy.get(mp); Method method = rmi.getMethod(); return (T) method.getAnnotation(clazz); } } |
在具体的拦截方法中, 通过JointPoint对象, 获取对应的调用方法/注解/参数等信息. 但这种方式不够简洁, 容易导致类型转换错误.
是否有一种办法, 能够做到所需参数的随叫随到, 而且避免了类型转换的坑.
答案是肯定的, 这为大英雄就是通知参数指定.
针对上面一个列子, 我们可以引入切面指示符@annotation类实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | @Target ({ElementType.TYPE, ElementType.METHOD}) @Retention (RetentionPolicy.RUNTIME) public @interface MyRequiresRoles { String[] value(); MyLogic logic() default MyLogic.AND; } @Aspect @Component public class MyShiroAdvice { /** * 定义切点, 用于角色的校验 */ @Pointcut ( "@annotation(com.springapp.mvc.myshiro.MyRequiresRoles)" ) public void checkRoles() { } // *) 通知参数指定, 通过指示符@annotation()指定了注解@MyRequiresRoles参数 @Before ( "checkRoles() && @annotation(mrp)" ) public void doCheckRole(MyRequiresRoles mrp) throws Exception { try { if ( !MyShiroHelper.validateRoles(mrp.value(), mrp.logic()) ) { throw new Exception( "access disallowed" ); } } catch (Exception e) { throw new Exception( "invalid state" ); } } } |
注: 对比上述两个代码, 功能不变, 却直接导入想要的注解信息(间接地规避了类型转换), 大大简化了代码编写.
我们再来一个切面指示符args的使用例子:
1 2 3 4 | @Before (value = "checkRoles() && args(k, v)" , argNames = "k, v" ) public void doCheckRole2(String k, String v) { // TODO } |
注: 只有满足切面checkRole()规则, 同时调用函数签名的参数列表为methodName(String, String), 才触发调用.
这个例子确实轻而易举的获取了调用函数的参数.
AspectJ指示符:
举例一下常见的一些指示符:
1 2 3 4 5 6 7 | execution:用于匹配方法执行的连接点. within:用于匹配指定类型内的方法执行. this :用于匹配当前AOP代理对象类型的执行方法, 注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配. target:用于匹配当前目标对象类型的执行方法, 注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配. args:用于匹配当前执行的方法传入的参数为指定类型的执行方法. @annotation :用于匹配当前执行方法持有指定注解的方法. reference pointcut:表示引用其他命名切入点. |
总结:
通过查阅一些资料, 以及自己的一些demo程序测试, 对Aspectj还是有一些收获的.
posted on 2018-07-24 17:30 mumuxinfei 阅读(792) 评论(0) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构