@Aspect - getSignature() 为空
@Aspect - getSignature() is null
我创建了简单的方面来计算特定方法的执行次数。我不得不说我是第一次做,所以可能不是很漂亮。
首先,我创建了类似的东西:
@Aspect
@Component
public class ItemAspect {
private Map<String, Integer> functionsCalls = new HashMap<>();
public ItemAspect(){
this.functionsCalls.put("ItemApiController.addItem()", 0);
this.functionsCalls.put("ItemApiController.getItems()", 0);
}
@Pointcut("execution(* io.swagger.api.ItemApiController.getItems(..))")
private void itemApiControllerEx(){ }
@After("itemApiControllerEx()")
public void doAfterItemApiControllerEx (JoinPoint joinPoint) {
this.functionsCalls.put(joinPoint.getSignature().toString(), this.functionsCalls.get(joinPoint.getSignature().toString())+1);
}
public Map<String, Integer> getFunctionsCalls () {
return functionsCalls; }
}
并统计了"getItems()"正确执行了多少次。 (是的,对于 "addItem()" 它也有效。)
但是我想统计这两种方法,所以我将代码转换为:
@Aspect
@Component
public class ItemAspect {
private Map<String, Integer> functionsCalls = new HashMap<>();
public ItemAspect(){
this.functionsCalls.put("ItemApiController.addItem()", 0);
this.functionsCalls.put("ItemApiController.getItems()", 0);
}
@Pointcut("execution(* io.swagger.api.ItemApiController.*(..))")
private void itemApiControllerEx(){ }
@After("itemApiControllerEx()")
public void doAfterItemApiControllerEx (JoinPoint joinPoint) {
this.functionsCalls.put(joinPoint.getSignature().toString(), this.functionsCalls.get(joinPoint.getSignature().toString())+1);
}
public Map<String, Integer> getFunctionsCalls () {
return functionsCalls; }
}
现在我在尝试获取签名时遇到 NullPointerException。有人能告诉我为什么会这样以及如何解决吗?
因为您没有分享您的应用程序代码 - 请了解 MCVE 是什么 - 我不能只 运行 它甚至看一看您的应用程序 class,但是从粗略地浏览一下您的方面代码,我看到您正在尝试访问这样的地图值:
functionsCalls.get(joinPoint.getSignature().toString())
现在我的猜测是你的目标 class 有超过你感兴趣的两种方法,并且在执行其中一种方法时你的地图访问 returns null
用于不存在的键值。所以 NullPointerException
不会来自尝试获取连接点签名,而是来自尝试增加 null
值,如下所示:
myMap.get(nonExistentValue) + 1 // null + 1 => NullPointerException
你应该更防御性地编程。
更新: 这里是纯 AspectJ 中的 MCVE,但 Spring AOP 中的方面语法是相同的。
package io.swagger.api;
public class ItemApiController {
public void addItem(Object item) {}
public Object getItems() {
return "dummy";
}
public void doSomethingElse() {}
}
package io.swagger.api;
import java.util.Map;
public class Application {
public static void main(String[] args) {
ItemApiController controller = new ItemApiController();
controller.addItem("A");
controller.getItems();
controller.addItem("B");
controller.doSomethingElse();
controller.addItem("C");
controller.getItems();
controller.doSomethingElse();
controller.addItem("C");
Map<String, Integer> statistics = ItemAspect.getStatistics();
for (String signature : statistics.keySet())
System.out.printf("%3d | %s%n", statistics.get(signature), signature);
}
}
package io.swagger.api;
import java.util.HashMap;
import java.util.Map;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class ItemAspect {
private static Map<String, Integer> methodCalls = new HashMap<>();
@Pointcut("execution(* io.swagger.api.ItemApiController.*(..))")
private void itemApiControllerEx() {}
@After("itemApiControllerEx()")
public void doAfterItemApiControllerEx(JoinPoint joinPoint) {
String signature = joinPoint.getSignature().toString();
methodCalls.computeIfAbsent(signature, newSignature -> 0);
methodCalls.put(signature, methodCalls.get(signature) + 1);
}
public static Map<String, Integer> getStatistics() {
return methodCalls;
}
}
您可能会注意到我重命名了一些东西,例如在 Java 中没有 "functions",只有方法。我也没有在不需要的地方一直使用 this
并删除了一些重复的代码。他们的方式是我用 0 动态初始化一个新的地图条目,如果它不存在,但如果你不知道如何 read/write lambda 表达式,你可能会觉得很奇怪。您可以通过
轻松地使用更 class 的编程风格
if (!methodCalls.containsKey(signature))
methodCalls.put(signature, 0);
此外,我保留了你使用 signature.toString()
作为映射键的想法,但你也可以使用 Signature
对象本身,或者先将它们转换为 MethodSignature
,然后再转换您可以选择要提取哪个部分进行打印。但这只是锦上添花。字符串比完整的签名对象占用更少的内存,所以这也很好。
如果您 运行 示例 Application
,您将获得此控制台输出:
2 | void io.swagger.api.ItemApiController.doSomethingElse()
2 | Object io.swagger.api.ItemApiController.getItems()
4 | void io.swagger.api.ItemApiController.addItem(Object)
更新 2: 还有另一种迭代地图的方法:使用它的条目(key/value 对):
for (Entry<String, Integer> entry : statistics.entrySet())
System.out.printf("%3d | %s%n", entry.getValue(), entry.getKey());
我创建了简单的方面来计算特定方法的执行次数。我不得不说我是第一次做,所以可能不是很漂亮。
首先,我创建了类似的东西:
@Aspect
@Component
public class ItemAspect {
private Map<String, Integer> functionsCalls = new HashMap<>();
public ItemAspect(){
this.functionsCalls.put("ItemApiController.addItem()", 0);
this.functionsCalls.put("ItemApiController.getItems()", 0);
}
@Pointcut("execution(* io.swagger.api.ItemApiController.getItems(..))")
private void itemApiControllerEx(){ }
@After("itemApiControllerEx()")
public void doAfterItemApiControllerEx (JoinPoint joinPoint) {
this.functionsCalls.put(joinPoint.getSignature().toString(), this.functionsCalls.get(joinPoint.getSignature().toString())+1);
}
public Map<String, Integer> getFunctionsCalls () {
return functionsCalls; }
}
并统计了"getItems()"正确执行了多少次。 (是的,对于 "addItem()" 它也有效。) 但是我想统计这两种方法,所以我将代码转换为:
@Aspect
@Component
public class ItemAspect {
private Map<String, Integer> functionsCalls = new HashMap<>();
public ItemAspect(){
this.functionsCalls.put("ItemApiController.addItem()", 0);
this.functionsCalls.put("ItemApiController.getItems()", 0);
}
@Pointcut("execution(* io.swagger.api.ItemApiController.*(..))")
private void itemApiControllerEx(){ }
@After("itemApiControllerEx()")
public void doAfterItemApiControllerEx (JoinPoint joinPoint) {
this.functionsCalls.put(joinPoint.getSignature().toString(), this.functionsCalls.get(joinPoint.getSignature().toString())+1);
}
public Map<String, Integer> getFunctionsCalls () {
return functionsCalls; }
}
现在我在尝试获取签名时遇到 NullPointerException。有人能告诉我为什么会这样以及如何解决吗?
因为您没有分享您的应用程序代码 - 请了解 MCVE 是什么 - 我不能只 运行 它甚至看一看您的应用程序 class,但是从粗略地浏览一下您的方面代码,我看到您正在尝试访问这样的地图值:
functionsCalls.get(joinPoint.getSignature().toString())
现在我的猜测是你的目标 class 有超过你感兴趣的两种方法,并且在执行其中一种方法时你的地图访问 returns null
用于不存在的键值。所以 NullPointerException
不会来自尝试获取连接点签名,而是来自尝试增加 null
值,如下所示:
myMap.get(nonExistentValue) + 1 // null + 1 => NullPointerException
你应该更防御性地编程。
更新: 这里是纯 AspectJ 中的 MCVE,但 Spring AOP 中的方面语法是相同的。
package io.swagger.api;
public class ItemApiController {
public void addItem(Object item) {}
public Object getItems() {
return "dummy";
}
public void doSomethingElse() {}
}
package io.swagger.api;
import java.util.Map;
public class Application {
public static void main(String[] args) {
ItemApiController controller = new ItemApiController();
controller.addItem("A");
controller.getItems();
controller.addItem("B");
controller.doSomethingElse();
controller.addItem("C");
controller.getItems();
controller.doSomethingElse();
controller.addItem("C");
Map<String, Integer> statistics = ItemAspect.getStatistics();
for (String signature : statistics.keySet())
System.out.printf("%3d | %s%n", statistics.get(signature), signature);
}
}
package io.swagger.api;
import java.util.HashMap;
import java.util.Map;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class ItemAspect {
private static Map<String, Integer> methodCalls = new HashMap<>();
@Pointcut("execution(* io.swagger.api.ItemApiController.*(..))")
private void itemApiControllerEx() {}
@After("itemApiControllerEx()")
public void doAfterItemApiControllerEx(JoinPoint joinPoint) {
String signature = joinPoint.getSignature().toString();
methodCalls.computeIfAbsent(signature, newSignature -> 0);
methodCalls.put(signature, methodCalls.get(signature) + 1);
}
public static Map<String, Integer> getStatistics() {
return methodCalls;
}
}
您可能会注意到我重命名了一些东西,例如在 Java 中没有 "functions",只有方法。我也没有在不需要的地方一直使用 this
并删除了一些重复的代码。他们的方式是我用 0 动态初始化一个新的地图条目,如果它不存在,但如果你不知道如何 read/write lambda 表达式,你可能会觉得很奇怪。您可以通过
if (!methodCalls.containsKey(signature))
methodCalls.put(signature, 0);
此外,我保留了你使用 signature.toString()
作为映射键的想法,但你也可以使用 Signature
对象本身,或者先将它们转换为 MethodSignature
,然后再转换您可以选择要提取哪个部分进行打印。但这只是锦上添花。字符串比完整的签名对象占用更少的内存,所以这也很好。
如果您 运行 示例 Application
,您将获得此控制台输出:
2 | void io.swagger.api.ItemApiController.doSomethingElse()
2 | Object io.swagger.api.ItemApiController.getItems()
4 | void io.swagger.api.ItemApiController.addItem(Object)
更新 2: 还有另一种迭代地图的方法:使用它的条目(key/value 对):
for (Entry<String, Integer> entry : statistics.entrySet())
System.out.printf("%3d | %s%n", entry.getValue(), entry.getKey());