SpringAOP中的JointPoint和ProceedingJoinPoint使用详解

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

ProceedingJoinPoint

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

JointPoint使用详解

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

  1. 切入点的方法名字及其参数
  2. 切入点方法标注的注解对象(通过该对象可以获取注解信息)
  3. 切入点目标对象(可以通过反射获取对象的类名,属性和方法名)
    注:有一点非常重要,Spring的AOP只能支持到方法级别的切入。换句话说,切入点只能是某个方法。

1 获取切入点所在目标对象 

Object targetObj =joinPoint.getTarget();

# 可以发挥反射的功能获取关于类的任何信息,例如获取类名如下
  String className = joinPoint.getTarget().getClass().getName();

 

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();

测试整合

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

1、自定义注解类:

@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;
}

 

2、切面类(使用JoinPoint)

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);
        }


    }
}

3、服务类

@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("没有注解的方法,不会被拦截");
    }
}

4、测试方法

  @Autowired
    TestServcie testServcie;
    @Test
    void  test6() throws Exception{

        testServcie.save("参数1字符串",33);
    }

5、测试结果

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

二、使用 ProceedingJoinPoint 

ProceedingJoinPoint继承JoinPoint子接口,它新增了两个用于执行连接点方法的方法: 
   java.lang.Object proceed() throws java.lang.Throwable:通过反射执行目标对象的连接点处的方法; 
   java.lang.Object proceed(java.lang.Object[] args) throws java.lang.Throwable:通过反射执行目标对象连接点处的方法,不过使用新的入参替换原来的入参。

1、自定义注解类:

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PromotionApiMonitor {
	/**
	 * 媒体类型
	 * @return
	 */
	MediaKeyValue mediaCode();

	/**
	 *
	 * @return
	 */
	TaskTypeEnum taskType();
}

2、2切面类(使用 ProceedingJoinPoint 

@Slf4j
@Component
@Aspect
public class PromotionMonitorAspect {


	/**
	 * 拦截入口下所有的方法
	 */
	@Pointcut("execution(* com.leiting.ads.controller.promote.media.gdt..*.*(..)) " +
			"|| execution(* com.leiting.ads.controller.promote.media.toutiao..*.*(..))")
	public void pointCut() {
	}

	/**
	 * 拦截处理
	 *
	 * @param point point 信息
	 * @return result
	 */
	@Around("pointCut()")
	public Object around(ProceedingJoinPoint point) throws Throwable {
		MethodSignature sign = (MethodSignature) point.getSignature();
		Method method = sign.getMethod();
		PromotionApiMonitor annotation = method.getAnnotation(PromotionApiMonitor.class);

       //获得注解运行成功后返回值(详情可参考下方设定)
		Object processResult = point.proceed();
		String processResultStr = processResult.toString();
		JsonObject respJson=new JsonParser().parse(processResultStr).getAsJsonObject();
		int status = respJson.get("status").getAsInt();

		if(status == 0){
			return processResult;
		}
		MediaKeyValue mediaKeyValue = annotation.mediaCode();
		TaskTypeEnum taskTypeEnum = annotation.taskType();
		JsonObject message = respJson.get("message").getAsJsonObject();
		JsonElement resultJsonElement = message.get("result");

		JSONArray jsonArray = new JSONArray();
		if(resultJsonElement != null) {
			String resultStr = resultJsonElement.toString();
			jsonArray = JSONObject.parseArray(resultStr);
		}else {
			JSONObject jsonObject = JSONObject.parseObject(message.toString());
			jsonArray.add(jsonObject);
		}


	}
}

三、controller类

  @PostMapping("modify_budget")
    @PromotionApiMonitor(mediaCode = MediaKeyValue.JRTT_MEDIA,taskType = TaskTypeEnum.MODIFY_ACCOUNT_BUGET)
    public Object modifyBudgetBatch(@RequestBody List<AccountBudgetModifyVo> accountBudgetModifyVoList) {
        return touTiaoAccountPromoteService.modifyBudgetBatch(accountBudgetModifyVoList);
    }




//return返回类型是:
{
    "status": 0,
    "message": "数据拉取完成"
}
类似数据包

  • 6
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值