首页 > 其他分享 >Spring中的AOP——在Advice方法中获取目标方法的参数

Spring中的AOP——在Advice方法中获取目标方法的参数

时间:2022-12-01 14:07:34浏览次数:34  
标签:point Spring Advice System com AOP println 方法 out

  • Object[] getArgs:返回目标方法的参数

  • Signature getSignature:返回目标方法的签名

  • Object getTarget:返回被织入增强处理的目标对象

  • Object getThis:返回AOP框架为目标对象生成的代理对象

注意:当使用@Around处理时,我们需要将第一个参数定义为ProceedingJoinPoint类型,该类是JoinPoint的子类。

下面的切面类(依然放在com.abc.advice包中)中定义了Before、Around、AfterReturning和After 4中增强处理,并分别在4种增强处理中访问被织入增强处理的目标方法、目标方法的参数和被织入增强处理的目标对象等:

package com.abc.advice;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.After;

import org.aspectj.lang.annotation.AfterReturning;

import org.aspectj.lang.annotation.Around;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Before;

@Aspect

public class AdviceTest {

@Around("execution(* com.abc.service..many(..))")

public Object process(ProceedingJoinPoint point) throws Throwable {

System.out.println("@Around:执行目标方法之前...");

//访问目标方法的参数:

Object[] args = point.getArgs();

if (args != null && args.length > 0 && args[0].getClass() == String.class) {

args[0] = "改变后的参数1";

}

//用改变后的参数执行目标方法

Object returnValue = point.proceed(args);

System.out.println("@Around:执行目标方法之后...");

System.out.println("@Around:被织入的目标对象为:" + point.getTarget());

return "原返回值:" + returnValue + ",这是返回结果的后缀";

}

@Before("execution(* com.abc.service..many(..))")

public void permissionCheck(JoinPoint point) {

System.out.println("@Before:模拟权限检查...");

System.out.println("@Before:目标方法为:" +

point.getSignature().getDeclaringTypeName() +

"."

  • point.getSignature().getName());

System.out.println("@Before:参数为:" + Arrays.toString(point.getArgs()));

System.out.println("@Before:被织入的目标对象为:" + point.getTarget());

}

@AfterReturning(pointcut="execution(* com.abc.service..many(..))",

returning="returnValue")

public void log(JoinPoint point, Object returnValue) {

System.out.println("@AfterReturning:模拟日志记录功能...");

System.out.println("@AfterReturning:目标方法为:" +

point.getSignature().getDeclaringTypeName() +

"." + point.getSignature().getName());

System.out.println("@AfterReturning:参数为:" +

Arrays.toString(point.getArgs()));

System.out.println("@AfterReturning:返回值为:" + returnValue);

System.out.println("@AfterReturning:被织入的目标对象为:" + point.getTarget());

}

@After("execution(* com.abc.service..many(..))")

public void releaseResource(JoinPoint point) {

System.out.println("@After:模拟释放资源...");

System.out.println("@After:目标方法为:" +

point.getSignature().getDeclaringTypeName() +

"." + point.getSignature().getName());

System.out.println("@After:参数为:" + Arrays.toString(point.getArgs()));

System.out.println("@After:被织入的目标对象为:" + point.getTarget());

}

}

在AdviceManager类中增加以下内容:

//将被AdviceTest的各种方法匹配

public String manyAdvices(String param1, String param2) {

System.out.println("方法:manyAdvices");

return param1 + " 、" + param2;

}

在com.abc.main.AOPTest中加入方法的调用,触发切点:

String result = manager.manyAdvices("aa", "bb");

System.out.println("Test方法中调用切点方法的返回值:" + result);

下面是执行结果:

@Around:执行目标方法之前...

@Before:模拟权限检查...

@Before:目标方法为:com.abc.service.AdviceManager.manyAdvices

@Before:参数为:[改变后的参数1, bb]

@Before:被织入的目标对象为:com.abc.service.AdviceManager@1dfc617e

方法:manyAdvices

@Around:执行目标方法之后...

@Around:被织入的目标对象为:com.abc.service.AdviceManager@1dfc617e

@After:模拟释放资源...

@After:目标方法为:com.abc.service.AdviceManager.manyAdvices

@After:参数为:[改变后的参数1, bb]

@After:被织入的目标对象为:com.abc.service.AdviceManager@1dfc617e

@AfterReturning:模拟日志记录功能...

@AfterReturning:目标方法为:com.abc.service.AdviceManager.manyAdvices

@AfterReturning:参数为:[改变后的参数1, bb]

@AfterReturning:返回值为:原返回值:改变后的参数1 、 bb,这是返回结果的后缀

@AfterReturning:被织入的目标对象为:com.abc.service.AdviceManager@1dfc617e

Test方法中调用切点方法的返回值:原返回值:改变后的参数1 、bb,这是返回结果的后缀

从结果中可以看出:在任何一个织入的增强处理中,都可以获取目标方法的信息。另外,Spring AOP采用和AspectJ一样的有限顺序来织入增强处理:在“进入”连接点时,最高优先级的增强处理将先被织入(所以给定的两个Before增强处理中,优先级高的那个会先执行);在“退出”连接点时,最高优先级的增强处理会最后被织入(所以给定的两个After增强处理中,优先级高的那个会后执行)。当不同的切面中的多个增强处理需要在同一个连接点被织入时,Spring AOP将以随机的顺序来织入这些增强处理。如果应用需要指定不同切面类里的增强处理的优先级,Spring提供了如下两种解决方案:

  • 让切面类实现org.springframework.core.Ordered接口:实现该接口只需要实现一个int getOrder()方法,该方法返回值越小,优先级越高

  • 直接使用@Order注解来修饰一个切面类:使用这个注解时可以配置一个int类型的value属性,该属性值越小,优先级越高

优先级高的切面类里的增强处理的优先级总是比优先级低的切面类中的增强处理的优先级高。例如:优先级为1的切面类Bean1包含了@Before,优先级为2的切面类Bean2包含了@Around,虽然@Around优先级高于@Before,但由于Bean1的优先级高于Bean2的优先级,因此Bean1中的@Before先被织入。

同一个切面类里的两个相同类型的增强处理在同一个连接点被织入时,Spring AOP将以随机的顺序来织入这两个增强处理,没有办法指定它们的织入顺序。如果确实需要保证它们以固有的顺序被织入,则可以考虑将多个增强处理压缩为一个增强处理;或者将不同增强处理重构到不同切面中,通过在切面级别上定义顺序。

如果只要访问目标方法的参数,Spring还提供了一种更加简洁的方法:我们可以在程序中使用args来绑定目标方法的参数。如果在一个args表达式中指定了一个或多个参数,该切入点将只匹配具有对应形参的方法,且目标方法的参数值将被传入增强处理方法。下面辅以例子说明:

package com.abc.advice;

import java.util.Date;

import org.aspectj.lang.annotation.AfterReturning;

import org.aspectj.lang.annotation.Aspect;

@Aspect

public class AccessArgAdviceTest {

@AfterReturning(

pointcut="execution(* com.abc.service..access(..)) && args(time, name)",

returning="returnValue")

public void access(Date time, Object returnValue, String name) {

System.out.println("目标方法中的参数String = " + name);

System.out.println("目标方法中的参数Date = " + time);

System.out.println("目标方法的返回结果returnValue = " + returnValue);

}

}

上面的程序中,定义pointcut时,表达式中增加了args(time, name)部分,意味着可以在增强处理方法(access方法)中定义time和name两个属性——这两个形参的类型可以随意指定,但一旦指定了这两个参数的类型,则这两个形参类型将用于限制该切入点只匹配第一个参数类型为Date,第二个参数类型为name的方法(方法参数个数和类型若有不同均不匹配)。

注意,在定义returning的时候,这个值(即上面的returning="returnValue"中的returnValue)作为增强处理方法的形参时,位置可以随意,即:如果上面access方法的签名可以为

public void access(Date time, Object returnValue, String name)

也可以为

标签:point,Spring,Advice,System,com,AOP,println,方法,out
From: https://blog.51cto.com/u_15767203/5901835

相关文章

  • Spring Cloud GCP(二)
    6.4.反应式流订阅者还可以获取由订阅支持的反应式流。为此,必须将项目反应器依赖项()添加到项目中。然后,Pub/Sub启动器和项目反应器依赖项的组合将使abean可用,然后可用......
  • vscode+springboot+gradle
    vscode+springboot+gradle项目搭建demo目标:项目中抛弃所有xml格式文件啰嗦:  一直在用maven作为项目的依赖包管理,最近看到基于Java17的Springframwork6......
  • springboot 项目自动重启脚本及注册方式
    创建脚本文件/etc/init.d/prs_xml.jar 添加脚本chkconfig-add prs_xml.jar查看服务列表chkconfig--list 启动服务chkconfigprs_xml.jaron设置启动等级chk......
  • Spring Cloud Gateway(网关)
    该项目提供了一个建立在Spring生态系统之上的API网关,包括:Spring5、SpringBoot2和ProjectReactor。SpringCloudGateway旨在提供一种简单而有效的方法来路由到API......
  • Spring Cloud Gateway—网关(二)
    7.全局过滤器该接口具有与相同的签名。这些是有条件地应用于所有路由的特殊过滤器。​​GlobalFilter​​​​GatewayFilter​​此接口及其用法可能会在未来的里程碑版本......
  • Spring Cloud 云原生应用
    云原生​是一种应用程序开发风格,鼓励在持续交付和价值驱动开发领域轻松采用最佳实践。一个相关的学科是构建12因素应用程序,其中开发实践与交付和运营目标保持一致—例如......
  • Spring Boot中使用分页插件PageHelper
    分页一、分页原理页码:pageNumpageNum从1开始,比如第1页、第2页......每页显示的记录条数:pageSize实际上每一次在进行分页请求发送的时候,都是要发送两个数据的:......
  • SpringBoot使用restTemplate远程访问时报错
    错误场景SpringBoot使用restTemplate远程访问时报错java.lang.IllegalStateException:Noinstancesavailableforxxx解决方案这个报错一般会出现在使用了负载均衡,如:......
  • springmvc工作流程
    springmvc工作原理图精简版:springmvc工作流程1、用户向服务端发送一次请求,这个请求会先到前端控制器DispatcherServlet(也叫中央控制器)。2、DispatcherServlet接收......
  • SpringBoot 3.0 新特性,内置声明式 HTTP 客户端
    httpinterface从Spring6和SpringBoot3开始,Spring框架支持将远程HTTP服务代理成带有特定注解的Javahttpinterface。类似的库,如OpenFeign和Retrofit仍然......