Spring Aop如何给Advice传递参数

给Advice传递参数

Advice除了可以接收JoinPoint(非Around Advice)或ProceedingJoinPoint(Around Advice)参数外,还可以直接接收与切入点方法执行有关的对象,比如切入点方法参数、切入点目标对象(target)、切入点代理对象(this)等。

1获取切入点方法参数

假设我们现在有一个id为userService的bean中定义了一个findById(int id)方法,我们希望定义一个Advice来拦截这个方法,并且把findById()的参数作为Advice处理方法的参数,即每次调用findById()传递的参数都将传递到Advice处理方法,那么我们可以如下这样定义。

@Before(value="bean(userService) && execution(* findById(java.lang.Integer)) && args(id)", argNames="id")
public void beforeWithParam(JoinPoint joinPoint, Integer id) {
  System.out.println(this.getClass().getName()+" ID is : " + id);
}

上面这种定义是非常精确的定义,我们通过表达式“bean(userService) && execution(* findById(java.lang.Integer))”就已经明确的指定了我们需要拦截的是id或name为userService的findById(Integer)方法,后面又加了一个args(id)是干什么用的呢?它的作用跟findById(Integer)是类似的,它表示我们的切入点方法必须只接收一个参数,而且这个参数的类型是和当前定义的Advice处理方法的参数id是相同类型的,在上面的示例中其实就是要求是Integer类型的;另外它还有一个非常重要的作用,通过这种指定后对应的Advice处理方法在执行时将接收到与之对应的切入点方法参数的值。在上面的示例中笔者特意给Advice处理方法加了一个JoinPoint参数是为了说明JoinPoint、ProceedingJoinPoint参数是可以直接定义在Advice方法的第一个参数,并且是可以与其它接收的参数共存的。其实如果我们不只是需要拦截findById(Integer)方法,而是需要拦截id为userService的bean中所有接收一个int/Integer参数的方法,那么我们可以把上面的配置简化为如下这样。

@Before(value="bean(userService) && args(id)", argNames="id")
public void beforeWithParam2(int id) {
   System.out.println(this.getClass().getName()+" ID is : " + id);
}

如果我们需要拦截的方法可能是有多个参数的,但我们只关注第一个参数,那我们可以把表达式调整为如下这样,只关注第一个参数为int/Integer类型的,并且在Advice方法中接收这个方法参数进行相应的处理。

@Before(value="bean(userService) && args(id,..)", argNames="id")
public void beforeWithParam2(int id) {
   System.out.println(this.getClass().getName()+" ID is : " + id);
}

2 argNames参数

我们可以看到在上述例子中我们都指定了@Before的argNames属性的值为id,那么这个argNames属性有什么作用呢?argNames属性是用于指定在表达式中应用的参数名与Advice方法参数是如何对应的,argNames中指定的参数名必须与表达式中的一致,可以与Advice方法参数名不一致;当表达式中使用了多个参数时,argNames中需要指定多个参数,多个参数之间以英文逗号分隔,这些参数的顺序必须与对应的Advice方法定义的参数顺序是一致的。

比如下面这个示例中,我们在Pointcut表达式中使用了name和sex两个参数,我们的Advice处理方法接收两个参数,分别是sex1和name1,我们希望Pointcut表达式中的name参数是对应的Advice处理方法的第二个参数,即name1,希望Pointcut表达式中的sex参数是对应的Advice处理方法的第一个参数,即sex1,那么我们在指定@Before注解的argNames参数时必须定义name和sex参数与Advice处理方法参数的关系,且顺序要求与对应的处理方法的参数顺序一致,即哪个参数是需要与Advice处理方法的第一个参数匹配则把哪个参数放第一位,与第二个参数匹配的则放第二位,在我们的这个示例中就应该是sex放第一位,name放第二位。

@Before(value="bean(userService) && args(name, sex)", argNames="sex, name")
public void beforeWithParam3(int sex1, String name1) {
  System.out.println("sex is : " + sex1);
  System.out.println("name is : " + name1);
}

@Before注解的argNames参数不是必须的,它只有在我们编译的字节码中不含DEBUG信息或Pointcut表达式中使用的参数名与Advice处理方法的参数名不一致时才需要。所以在编译的字节码中包含DEBUG信息且Advice参数名与Pointcut表达式中使用的参数名一致时,我们完全可以把argNames参数省略。如果表达式里面使用了多个参数,那么这些参数在表达式中的顺序可以与Advice方法对应参数的顺序不一致,例如下面这个样子。

@Before(value="bean(userService) && args(id)")
public void beforeWithParam2(int id) {
  System.out.println(this.getClass().getName()+" ID is : " + id);
}

3 获取this对象

this对象就是Spring生成的bean的那个代理对象。如下示例就是Advice方法接收this对象,我们给Advice方法指定一个需要拦截的this对象类型的参数,然后在表达式中使用this类型的表达式定义,表达式中定义的对应类型指定为Advice方法参数。

@Before("this(userService)")
public void beforeWithParam4(IUserService userService) {
    //this对象应该是一个代理对象
    System.out.println(this.getClass().getName()+"==============传递this对
    象: " + userService.getClass());
}

4 混合使用

我们的Advice方法可以同时接收多个目标方法参数,与此同时它也可以接收this等对象,即它们是可以混合使用的。下面这个示例中我们就同时接收了this对象和目标方法int/Interger类型的参数。

@Before("this(userService) && args(id)")
public void beforeWithParam5(IUserService userService, int id) {
    System.out.println(this.getClass().getName()+"===========" + id +
    "==============" + userService.getClass());
}

5 获取target对象

获取target对象也比较简单,只需要把表达式改为target类型的表达式即可。

@Before("target(userService)")
public void beforeWithParam6(IUserService userService) {
    System.out.println(this.getClass().getName()+"==============传递
    target对象: " + userService.getClass());
}

6 获取注解对象

当我们的Pointcut表达式类型是通过注解匹配时,我们也可以在Advice处理方法中获取匹配的注解对象,如下面这个示例,其它如使用@target等是类似的。

@Before("@annotation(annotation)")
public void beforeWithParam7(MyAnnotation annotation) {
    System.out.println(this.getClass().getName()+"==============传递标
    注在方法上的annotation: " + annotation.annotationType().getName());
}

7 泛型参数

有的时候我们的Advice方法需要接收的切入点方法参数定义的不是具体的类型,而是一个泛型,这种情况下怎么办呢?可能你会想那我就把对应的Advice方法参数定义为Object类型就好了,反正所有的类型都可以转换为Object类型。对的,这样是没有错的,但是说如果你只想拦截某种具体类型的参数调用时就可以不用把Advice方法参数类型定义为Object了,这样还得在方法体里面进行判断,我们可以直接把Advice方法参数类型定义为我们想拦截的方法参数类型。比如我们有下面这样一个使用了泛型的方法定义,我们希望只有在调用testParam方法时传递的参数类型是Integer类型时才进行拦截。

public <T> void testParam(T param);

那这个时候我们就可以把我们的Advice的表达式定义为如下这样,前者精确定义接收方法名为testParam,返回类型为void,后者精确定义方法参数为一个Integer类型的参数,其实前者也可以定义为“execution(void testParam(Integer))”。看到这你可能会想,为什么不直接把表达式定义为“execution(void testParam(param))”呢?因为execution是不支持Advice方法参数绑定的,基本上支持Advice参数绑定的就只有this、target、args以及对应的注解形式加@annotation。

@Before("execution(void testParam(..)) && args(param)")
public void beforeWithParam8(Integer param) {
    System.out.println("pointcut expression[args(param)]--------------param:" +
    param);
}

以上就是常用的传递参数给Advice处理方法的方式,有一些示例可能没有讲到,比如@target这种,这些其实都是类似的。包括上面我们都是以@Before这种Advice来讲的,其实其它的Advice在接收参数的时候也是类似的。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Spring AOP访问目标方法的参数操作示例

    本文实例讲述了Spring AOP访问目标方法的参数操作.分享给大家供大家参考,具体如下: 一 配置 <?xml version="1.0" encoding="GBK"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns

  • Spring基于advisor配置aop过程解析

    1.目标类 package com.gec.target; public class Hadoop { public void eatting() { System.out.println("大象正在吃东西 1"); try { //耗时5秒 Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } } } 2.增强类,此类必须要实现增强方位接口 package com.gec.advic

  • 详解使用Spring AOP和自定义注解进行参数检查

    引言 使用SpringMVC作为Controller层进行Web开发时,经常会需要对Controller中的方法进行参数检查.本来SpringMVC自带@Valid和@Validated两个注解可用来检查参数,但只能检查参数是bean的情况,对于参数是String或者Long类型的就不适用了,而且有时候这两个注解又突然失效了(没有仔细去调查过原因),对此,可以利用Spring的AOP和自定义注解,自己写一个参数校验的功能. 代码示例 注意:本节代码只是一个演示,给出一个可行的思路,并非完整的解决

  • SpringMVC @ControllerAdvice使用场景

    这篇文章主要介绍了SpringMVC @ControllerAdvice使用场景,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 @ControllerAdvice ,很多初学者可能都没有听说过这个注解,实际上,这是一个非常有用的注解,顾名思义,这是一个增强的 Controller.使用这个 Controller ,可以实现三个方面的功能: 全局异常处理 全局数据绑定 全局数据预处理 灵活使用这三个功能,可以帮助我们简化很多工作,需要注意的是,这

  • Springboot使用@Valid 和AOP做参数校验及日志输出问题

    项目背景 最近在项目上对接前端的的时候遇到了几个问题 1.经常要问前端要请求参数 2.要根据请求参数写大量if...else,代码散步在 Controller 中,影响代码质量 3.为了解决问题1,到处记日志,导致到处改代码 解决方案 为了解决这类问题,我使用了@Valid 做参数校验,并使用AOP记录前端请求日志 1.Bean实体类增加注解 对要校验的实体类增加注解,如果实体类中有List结构,就在List上加@Valid @Valid注解 注解 备注 @Null 只能为null @NotNu

  • Spring如何基于aop实现操作日志功能

    1. 在pom中添加所需依赖 创建一个springboot工程,添加所需要的依赖,持久化用的是mybatis <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--springboot aop依赖--> <dependency

  • SpringBoot @ControllerAdvice 拦截异常并统一处理

    在spring 3.2中,新增了@ControllerAdvice 注解,可以用于定义@ExceptionHandler.@InitBinder.@ModelAttribute,并应用到所有@RequestMapping中.参考:@ControllerAdvice 文档 一.介绍 创建 MyControllerAdvice,并添加 @ControllerAdvice注解. package com.sam.demo.controller; import org.springframework.ui

  • Spring注解@RestControllerAdvice原理解析

    这篇文章主要介绍了Spring注解@RestControllerAdvice原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 前言 前段时间部门搭建新系统,需要出异常后统一接口的返回格式,于是用到了Spring的注解@RestControllerAdvice.现在把此注解的用法总结一下. 用法 首先定义返回对象ResponseDto package com.staff.points.common; import lombok.Data;

  • 关于Spring AOP使用时的一些问题汇总

    在使用AOP的时候遇到了一些问题,特此记录一下 首先写一个常用的AOP切片 切片类AopLog package com.mantis.aop.aspect; import com.fasterxml.jackson.databind.ObjectMapper; import com.mantis.aop.common.util.DataUtil; import eu.bitwalker.useragentutils.UserAgent; import org.aspectj.lang.JoinP

  • Spring Aop如何给Advice传递参数

    给Advice传递参数 Advice除了可以接收JoinPoint(非Around Advice)或ProceedingJoinPoint(Around Advice)参数外,还可以直接接收与切入点方法执行有关的对象,比如切入点方法参数.切入点目标对象(target).切入点代理对象(this)等. 1获取切入点方法参数 假设我们现在有一个id为userService的bean中定义了一个findById(int id)方法,我们希望定义一个Advice来拦截这个方法,并且把findById()

  • Spring Boot/VUE中路由传递参数的实现代码

    在路由时传递参数,一般有两种形式,一种是拼接在url地址中,另一种是查询参数.如:http://localhost:8080/router/tang/101?type=spor&num=12.下面根据代码看一下,VUE 和 Spring Boot 中各自是如何处理传递和接受参数的. Spring Boot package com.tang.demo1.controller; import org.springframework.web.bind.annotation.*; @RestContro

  • spring AOP实现@Around输出请求参数和返回参数

    目录 @Around输出请求参数和返回参数 先把我的打印日志代码贴出来 测试 spring AOP中Around切面处理参数 解决的办法 具体的代码 @Around输出请求参数和返回参数 spring 的AOP是通过cglib动态代理和jdk的动态代理实现的. 先把我的打印日志代码贴出来 package com.zhd.exploit.api.config; import java.util.HashMap; import java.util.Map; import java.util.UUID

  • Spring Aop 如何获取参数名参数值

    前言: 有时候我们在用Spring Aop面向切面编程,需要获取连接点(JoinPoint)方法参数名.参数值. 环境: Mac OSX Intellij IDEA Spring Boot 2x Jdk 1.8x Code: package com.example.aopdemo.aop; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.a

  • Spring Mvc中传递参数方法之url/requestMapping详解

    前言 相信大家在使用spring的项目中,前台传递参数到后台是经常遇到的事, 我们必须熟练掌握一些常用的参数传递方式和注解的使用,本文将给大家介绍关于Spring Mvc中传递参数方法之url/requestMapping的相关内容,分享出来供大家参考学习,话不多说,直接上正文. 方法如下 1. @requestMapping: 类级别和方法级别的注解, 指明前后台解析的路径. 有value属性(一个参数时默认)指定url路径解析,method属性指定提交方式(默认为get提交) @Reques

  • 解决 Spring RestTemplate post传递参数时报错问题

    今天跟同事接口联调,使用RestTemplate请求服务端的post接口(使用python开发).诡异的是,post请求,返回500 Internal Server Error,而使用get请求,返回正常.代码如下: HashMap<String, Object> hashMap = Maps.newHashMap(); hashMap.put("data", JSONObject.toJSONString(params)); url = "http://mydo

  • 聊聊Spring AOP @Before @Around @After等advice的执行顺序

    用过spring框架进行开发的人,多多少少会使用过它的AOP功能,都知道有@Before.@Around和@After等advice. 最近,为了实现项目中的输出日志和权限控制这两个需求,我也使用到了AOP功能. 我使用到了@Before.@Around这两个advice.但在,使用过程中,却对它们的执行顺序并不清楚. 为了弄清楚在不同情况下,这些advice到底是以怎么样的一个顺序进行执行的,我作了个测试,在此将其记录下来,以供以后查看. 前提 对于AOP相关类(aspect.pointcut

  • 如何使用Spring AOP预处理Controller的参数

    Spring AOP预处理Controller的参数 实际编程中,可能会有这样一种情况,前台传过来的参数,我们需要一定的处理才能使用 比如有这样一个Controller @Controller public class MatchOddsController { @Autowired private MatchOddsServcie matchOddsService; @RequestMapping(value = "/listOdds", method = RequestMethod

随机推荐