SpringAOP中的JointPoint和ProceedingJoinPoint使用详解(附带详细示例)

您所在的位置:网站首页 怎么读advise SpringAOP中的JointPoint和ProceedingJoinPoint使用详解(附带详细示例)

SpringAOP中的JointPoint和ProceedingJoinPoint使用详解(附带详细示例)

2023-10-26 10:36| 来源: 网络整理| 查看: 265

概念 Joint Point

JointPoint是程序运行过程中可识别的点,这个点可以用来作为AOP切入点。JointPoint对象则包含了和切入相关的很多信息。比如切入点的对象,方法,属性等。我们可以通过反射的方式获取这些点的状态和信息,用于追踪tracing和记录logging应用信息。

Pointcut

pointcut 是一种程序结构和规则,它用于选取join point并收集这些point的上下文信息。 pointcut通常包含了一系列的Joint Point,我们可以通过pointcut来同时操作jointpoint。单从概念上,可以把Pointcut当做jointpoint的集合。

JointPoint和ProceedingJoinPoint区别 JointPoint

通过JpointPoint对象可以获取到下面信息

# 返回目标对象,即被代理的对象 Object getTarget(); # 返回切入点的参数 Object[] getArgs(); # 返回切入点的Signature Signature getSignature(); # 返回切入的类型,比如method-call,field-get等等,感觉不重要 String getKind(); ProceedingJoinPoint

Proceedingjoinpoint 继承了 JoinPoint。是在JoinPoint的基础上暴露出 proceed 这个方法。proceed很重要,这个是aop代理链执行的方法。

环绕通知=前置+目标方法执行+后置通知,proceed方法就是用于启动目标方法执行的

暴露出这个方法,就能支持 aop:around 这种切面(而其他的几种切面只需要用到JoinPoint,,这也是环绕通知和前置、后置通知方法的一个最大区别。这跟切面类型有关), 能决定是否走代理链还是走自己拦截的其他逻辑。建议看一下 JdkDynamicAopProxy的invoke方法,了解一下代理链的执行原理。

注:ProceedingJoinPoint is only supported for around advice

JointPoint使用详解

这里详细介绍JointPoint的方法,这部分很重要是coding核心参考部分。开始之前我们思考一下,我们到底需要获取切入点的那些信息。我的思考如下

切入点的方法名字及其参数切入点方法标注的注解对象(通过该对象可以获取注解信息)切入点目标对象(可以通过反射获取对象的类名,属性和方法名)

注:有一点非常重要,Spring的AOP只能支持到方法级别的切入。换句话说,切入点只能是某个方法。

针对以上的需求JDK提供了如下API

1 获取切入点所在目标对象 Object targetObj =joinPoint.getTarget(); # 可以发挥反射的功能获取关于类的任何信息,例如获取类名如下 String className = joinPoint.getTarget().getClass().getName();

因为一个类有很多方法,为了获取具体切入点所在的方法可以通过如下API

2.获取切入点方法的名字

getSignature());是获取到这样的信息 :修饰符+ 包名+组件名(类名) +方法名

这里我只需要方法名

String methodName = joinPoint.getSignature().getName() 3. 获取方法上的注解

方法1:xxxxxx是注解名字

Signature signature = joinPoint.getSignature(); MethodSignature methodSignature = (MethodSignature) signature; Method method = methodSignature.getMethod(); if (method != null) { xxxxxx annoObj= method.getAnnotation(xxxxxx.class); } return null;

方法2:上面我们已经知道了方法名和类的对象,通过反射可以获取类的内部任何信息。

// 切面所在类 Object target = joinPoint.getTarget(); String methodName = joinPoint.getSignature().getName(); Method method = null; for (Method m : target.getClass().getMethods()) { if (m.getName().equals(methodName)) { method = m; // xxxxxx annoObj= method.getAnnotation(xxxxxx.class);同上 break; } } 4. 获取方法的参数

这里返回的是切入点方法的参数列表

Object[] args = joinPoint.getArgs(); 测试

眼见为实,测试一遍可以理解更深刻

@Target({ ElementType.PARAMETER, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ApiLog { /** * 模块 */ public String title() default ""; /** * 日志记录service实现 * @return */ public String logService() default "operLogServiceImpl"; /** * 是否保存请求的参数 */ public boolean isSaveRequestData() default true; /** * 是否追踪用户操作 * @return */ public boolean isTrack() default true; }

切面类

package com.kouryoushine.aop.test; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @Aspect @Component public class DemoAspect { //切入点:aopdemo报下所有对象的save方法 @Pointcut("execution(public * com.kouryoushine.aop.test.*.save*(..))") public void save(){ } /** * 需要在update操作前后分别获取更新前后的值 * @param * @return */ @AfterReturning("save()") public void afterReturn(JoinPoint joinPoint) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { //1.获取切入点所在目标对象 Object targetObj =joinPoint.getTarget(); System.out.println(targetObj.getClass().getName()); // 2.获取切入点方法的名字 String methodName = joinPoint.getSignature().getName(); System.out.println("切入方法名字:"+methodName); // 3. 获取方法上的注解 Signature signature = joinPoint.getSignature(); MethodSignature methodSignature = (MethodSignature) signature; Method method = methodSignature.getMethod(); if (method != null) { ApiLog apiLog= method.getAnnotation(ApiLog.class); System.out.println("切入方法注解的title:"+apiLog.title()); } //4. 获取方法的参数 Object[] args = joinPoint.getArgs(); for(Object o :args){ System.out.println("切入方法的参数:"+o); } } }

服务类

@Service public class TestServcie { @ApiLog(title = "注解的标题",isSaveRequestData = false) public void save(String parm1,int parm2){ System.out.println("执行目标对象的方法"+parm1+parm2); } public void update(){ System.out.println("没有注解的方法,不会被拦截"); } }

测试方法

@Autowired TestServcie testServcie; @Test void test6() throws Exception{ testServcie.save("参数1字符串",33); }

测试结果

com.kouryoushine.aop.test.TestServcie 切入方法名字:save 切入方法注解的title:注解的标题 切入方法的参数:参数1字符串 切入方法的参数:33 总结

如上,学习后觉得就这么点东西,如果不想明白每次都迷迷糊糊。大家加油



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3