Spring AOP 之三:通知(Advice)方法参数

原创
2017/06/30 20:28
阅读数 4.5K

简介

前面2篇文章我们重点介绍的是Spring AOP的流程和Pointcut表达式,我们处理一些简单的切面逻辑是没有问题了,在Pointcut表达式中介绍Pointcut表达式是为了在什么地方执行切面逻辑更加灵活,那么这篇文章介绍的通知参数处理就是为了让切面逻辑的做什么更加灵活。

JoinPoint

前面2篇介绍的切面逻辑基本上没有对被代理的逻辑本身做过多的操作,我们只是在被代理的逻辑(方法,连接点,Joinpoint)的前后执行切面逻辑。

如果我们要对被代理的逻辑(方法,连接点,Joinpoint)本身进行修改那应该怎么办呢?Spring AOP已经为我们提供了抽象Joinpoint。其实我们在前面的Around通知的时候已经简单的使用到了一点,不过使用的是Joinpoint的子类ProceedingJoinPoint。

我们可以通过Joinpoint来获取到代理的逻辑(方法,连接点,Joinpoint)的参数,代理对象(proxy),目标对象(target),方法签名等信息。

要使用JointPoint也很容易,会把JoinPoint对象传入代理方法(Advice方法,如@Before等通知注解的方法)的第一个参数。如果是Around通知出入的是ProceedingJoinPoint。

args

除了JoinPoint方式获取参数之外,还可以通过在Pointcut表达式中通过args绑定参数,并且还可以起到限制匹配参数的目的。

例如:

@Before("iAdviceParameter() && args(arg1,arg,..)")
public void joinPointParamterMethodBefore(String arg,Integer arg1){}

上面的表达式就可以把iAdviceParameter()匹配到的方法(Joinpoint,连接点)的参数传递到joinPointParamterMethodBefore方法中。注意匹配的方式只和args的顺序与类型相关,和joinPointParamterMethodBefore(Advice逻辑(方法))没有关系。

实例

上面的说明对于不是特别了解Spring AOP的同学可能过于抽象,所以下面还是通过一个实例来说明一下上面介绍的JoinPoint和args的用法。

下面的例子还是在原来之前的例子上进行了扩展,当然也可以单独的测试。下面还是先看一下目录结构。

目录结构

IAdviceParameter

public interface IAdviceParameter {
    
    String JoinPointParamterMethod(Integer arg1 ,String arg2,char arg3);

}

JoinPointParamterMethodImpl

import org.springframework.stereotype.Service;

import cn.freemethod.business.param.IAdviceParameter;

@Service
public class JoinPointParamterMethodImpl implements IAdviceParameter{

    @Override
    public String JoinPointParamterMethod(Integer arg1, String arg2,char arg3) {
        System.out.println(arg1);
        System.out.println(arg2);
        System.out.println(arg3);
        return "result";
    }

}

上面是2个业务逻辑相关的类,以便测试下面切面通知逻辑。

AdviceParameterAspect

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class AdviceParameterAspect {
    
    @Pointcut("this(cn.freemethod.business.param.IAdviceParameter)")
    public void iAdviceParameter(){}
    
 //@Before("iAdviceParameter() && args(arg,arg1,..)")
    public void joinPointParamterMethodBefore(String arg1,Integer arg){
        System.out.println("AdviceParameterAspect Advice Before:"+arg1);
        System.out.println("AdviceParameterAspect Advice Before:"+arg);
    }
    
    @After("iAdviceParameter()")
    public void joinPointParamterMethodAfter(JoinPoint jp){
        System.out.println("AdviceParameterAspect Advice After...");
        Object[] args = jp.getArgs();
        for (int i = 0; i < args.length; i++) {
            System.out.println(args[i]);
        }
        System.out.println("kind:"+jp.getKind());
        System.out.println("target:"+jp.getTarget());
        System.out.println("proxy:"+jp.getThis());
        System.out.println("signature:"+jp.getSignature());
        System.out.println("AdviceParameterAspect Advice After...");
    }
    
//    @Around("iAdviceParameter()")
    public void joinPointParamterMethodAround(ProceedingJoinPoint pjp){
        System.out.println("AdviceParameterAspect Advice Around...");
        Object[] args = pjp.getArgs();
        for (int i = 0; i < args.length; i++) {
            System.out.println(args[i]);
        }
        System.out.println("AdviceParameterAspect Advice Around...");
    }
    
//    @Around("iAdviceParameter()")
    public Object joinPointParamterMethodAroundWithResult(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("AdviceParameterAspect Advice Around...");
        Object[] args = pjp.getArgs();
        for (int i = 0; i < args.length; i++) {
            System.out.println(args[i]);
        }
        System.out.println("AdviceParameterAspect Advice Around...");
        return pjp.proceed();
    }

}

上面是一个切面逻辑类,这里我们把Pointcut直接写在了切面之中了。通过上面的例子可以看到我们通过JoinPoint获取到参数数组,获取目标类(target),获取代理类(proxy)和方法签名。

下面我们重点看一下下面的方法:

 @Before("iAdviceParameter() && args(arg,arg1,..)")
    public void joinPointParamterMethodBefore(String arg1,Integer arg){
        System.out.println("AdviceParameterAspect Advice Before:"+arg1);
        System.out.println("AdviceParameterAspect Advice Before:"+arg);
    }

我们指定在上面的例子中Pointcut会匹配到下面的方法:

public String JoinPointParamterMethod(Integer arg1, String arg2,char arg3) 

在Spring执行切面逻辑(方法joinPointParamterMethodBefore)的时候,就会把调用JoinPointParamterMethod的参数和Pointcut中的args列表按位置匹配。和参数的名字无关之后参数的顺序有关,当args的参数和joinPointParamterMethodBefore参数匹配的时候就之后参数的名字有关和顺序无关。args(arg,arg1,..)中最后的的..表示还有其他参数。

AspectConfig

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = {"cn.freemethod"})
public class AspectConfig {

}

上面是一个注解配置类。

AdviceParameterStart

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;

import cn.freemethod.business.param.IAdviceParameter;
import cn.freemethod.config.AspectConfig;


public class AdviceParameterStart {
    
    public static void main(String[] args) {
        AbstractApplicationContext  context = new AnnotationConfigApplicationContext(AspectConfig.class);
        IAdviceParameter bean = context.getBean(IAdviceParameter.class);
        String result = bean.JoinPointParamterMethod(1, "ok", 'c');
        System.out.println(result);
        context.close();
    }

}

上面一个是一个启动类。

注意:为了输出的清晰很多都是注释了,请根据实际要测试的方法取消注释。

参考

完整工程代码

Spring AOP 之一:基本概念与流程

Spring AOP 之二:Pointcut注解表达式

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部