我希望AspectJ在任何方法的所有调用上注入测量代码,在使用@Measured注释的字段上捕获方法的名称。 这就是我所拥有的:
@Pointcut("get(@my.annotation.Measured * *) && @annotation(measured)")
public void fieldAnnotatedWithMeasured(Measured measured) {}
@Around(value = "fieldAnnotatedWithMeasured(measured)", argNames = "joinPoint,measured")
public Object measureField(ProceedingJoinPoint joinPoint, Measured measured) throws Throwable {...}
用例:
public class A {
@Measured private Service service;
...
void call(){
service.call(); // here I want to measure the call() time and capture its name
}
这似乎只包含对字段的访问,而不是方法调用。我想捕获调用的方法名称instide建议。
答案 0 :(得分:1)
这不是您可以直接使用切入点执行的操作,因为您注意到get()
与call()
或execution()
切入点完全不同。在get()
完成之前,call()
个连接点已完全通过。此外,call()
不知道它被调用的目标对象是否恰好被分配给一个或多个(带注释的)类成员。
我认为你想要实现的目标在概念上是有问题的。您应该注释要测量的类或方法,而不是类成员。但是对于它的价值,我将向您介绍一个解决方案。警告:解决方案涉及手动记账和反思。因此,它有点慢,但可能仍然足够快,你的目的。您可以决定是否尝试一下。请注意,这个解决方案让我感到不安,因为它不像是AOP的良好应用。
好的,这是我们的测试设置:
字段注释:
package de.scrum_master.app;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Measured {}
以后可以使用的示例类:
package de.scrum_master.app;
public class MyClass {
private String name;
public MyClass(String name) {
super();
this.name = name;
}
@Override
public String toString() {
return "MyClass[" + name + "]";
}
void waitForAWhile() throws InterruptedException {
Thread.sleep(200);
}
}
使用示例类的驱动程序应用程序:
请注意四个成员中只有两个 - 一个原始成员和一个对象类型 - 如何被@Measured
注释而另外两个成员没有注释。我这样做是为了得到正面和负面的例子,以便看看方面是否正常工作。
另一个重要的事情是,只要不再将该对象分配给该成员,该对象就不应再报告以前分配给带注释的类成员的对象。即oldMyClass.waitForAWhile();
不应该被衡量。
package de.scrum_master.app;
public class Application {
String foo = "unmeasured";
@Measured String bar = "measured";
MyClass myClass1 = new MyClass("unmeasured");
@Measured MyClass myClass2 = new MyClass("measured");
void doSomething() throws InterruptedException {
foo.length();
bar.length();
myClass1.waitForAWhile();
myClass2.waitForAWhile();
MyClass oldMyClass = myClass2;
myClass2 = new MyClass("another measured");
// This call should not be reported by the aspect because the object
// is no longer assigned to a member annotated by @Measured
oldMyClass.waitForAWhile();
// This call should be reported for the new member value
myClass2.waitForAWhile();
}
public static void main(String[] args) throws InterruptedException {
new Application().doSomething();
}
}
<强>方面:强>
方面涉及两件事:簿记和测量。详细说明:
@Measured
字段时,它都会记录在一组measuredObjects
中,因为这是以后知道在该对象上调用方法的唯一方法,它真的应该衡量。before() : set()
建议中掌握新值,但遗憾的是没有直接的方法来掌握旧值。这就是为什么我们需要使用反射的丑陋小辅助方法getField(Signature signature)
才能找到答案。measuredObjects
集中删除未分配的对象。measuredObjects
与我实现它的方式不是线程安全的,但如果需要,您可以使用同步集合。call()
建议首先检查它是否可以在measuredObjects
中找到目标对象,如果不能,则停止执行。否则,它会测量方法调用的运行时间。这很简单。哦,顺便说一句,我在这里使用更清晰,更具表现力的原生AspectJ语法,而不是丑陋的注释风格。如果您有任何问题,请告诉我。
package de.scrum_master.app;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Set;
import org.aspectj.lang.Signature;
import org.aspectj.lang.SoftException;
import de.scrum_master.app.Measured;
public aspect MyAspect {
private Set<Object> measuredObjects = new HashSet<>();
before(Measured measured, Object newValue, Object object) :
set(* *) &&
@annotation(measured) &&
args(newValue) &&
target(object)
{
try {
Field field = getField(thisJoinPoint.getSignature());
Object oldValue = field.get(object);
System.out.println(thisJoinPoint);
System.out.println(" old value = " + oldValue);
System.out.println(" new value = " + newValue);
measuredObjects.remove(oldValue);
measuredObjects.add(newValue);
}
catch (Exception e) {
throw new SoftException(e);
}
}
Object around(Object object) :
call(* *(..)) &&
target(object) &&
!within(MyAspect)
{
if (!measuredObjects.contains(object))
return proceed(object);
long startTime = System.nanoTime();
Object result = proceed(object);
System.out.println(thisJoinPoint);
System.out.println(" object = " + object);
System.out.println(" duration = " + (System.nanoTime() - startTime) / 1e6 + " ms");
return result;
}
private Field getField(Signature signature) throws NoSuchFieldException {
Field field = signature.getDeclaringType().getDeclaredField(signature.getName());
field.setAccessible(true);
return field;
}
}
控制台日志:
set(String de.scrum_master.app.Application.bar)
old value = null
new value = measured
set(MyClass de.scrum_master.app.Application.myClass2)
old value = null
new value = MyClass[measured]
call(int java.lang.String.length())
object = measured
duration = 0.080457 ms
call(void de.scrum_master.app.MyClass.waitForAWhile())
object = MyClass[measured]
duration = 200.472326 ms
set(MyClass de.scrum_master.app.Application.myClass2)
old value = MyClass[measured]
new value = MyClass[another measured]
call(void de.scrum_master.app.MyClass.waitForAWhile())
object = MyClass[another measured]
duration = 200.461208 ms
如您所见,方面行为正确。它只报告对象MyClass[measured]
上的方法调用,同时将其分配给@Measured
字段,但是在已经取消分配并由{{1}替换之后调用方法时则不会}。随后正确报告后者。您还可以看到方面如何在字符串MyClass[another measured]
等原语中精美地工作。
享受!