是否有可能排除AspectJ切入点中的fiel集,从而使Java11中的最终字段不会出现故障?
在编织以下方面时(完整的示例如下:https://github.com/DaGeRe/aspect-final-example):
@Pointcut("!within(de.aspectjtest..*)")
public void notWithinAspect() {
}
@Pointcut("!set(private * *)")
public void noSet() {
}
@Around("notWithinAspect() && noSet()")
public Object aroundStuff(final ProceedingJoinPoint thisJoinPoint, final EnclosingStaticPart thisEnclosingJoinPoint)
throws Throwable {
System.out.println("=== Call: " + thisJoinPoint.getSignature() + " " + thisJoinPoint.getKind());
System.out.println(thisJoinPoint.getSourceLocation() + " " + thisJoinPoint.getStaticPart());
System.out.println(thisJoinPoint.toLongString());
return thisJoinPoint.proceed();
}转到
class FinalFieldConstructorExample {
private final Integer parameters = 5;
public Integer getParameters() {
return parameters;
}
}
public class MainWithError {
public static void main(String[] args) {
FinalFieldConstructorExample example = new FinalFieldConstructorExample();
System.out.println(example.getParameters());
}
}通过java -cp target/test-0.1-SNAPSHOT.jar -javaagent:../aspect/target/aspectjtest-0.1-SNAPSHOT.jar de.test.MainWithError我得到
Exception in thread "main" java.lang.IllegalAccessError: Update to non-static final field de.test.FinalFieldConstructorExample.parameters attempted from a different method (init$_aroundBody2) than the initialize
r method <init>
at de.test.FinalFieldConstructorExample.init$_aroundBody2(MainWithError.java:5)
at de.test.FinalFieldConstructorExample$AjcClosure3.run(MainWithError.java:1)
at org.aspectj.runtime.reflect.JoinPointImpl.proceed(JoinPointImpl.java:167)
at de.aspectjtest.ExampleAspect.aroundStuff(ExampleAspect.java:27)
at de.test.FinalFieldConstructorExample.<init>(MainWithError.java:3)
at de.test.MainWithError.init$_aroundBody2(MainWithError.java:15)
at de.test.MainWithError$AjcClosure3.run(MainWithError.java:1)
at org.aspectj.runtime.reflect.JoinPointImpl.proceed(JoinPointImpl.java:167)
at de.aspectjtest.ExampleAspect.aroundStuff(ExampleAspect.java:27)
at de.test.MainWithError.main_aroundBody10(MainWithError.java)
at de.test.MainWithError$AjcClosure11.run(MainWithError.java:1)
at org.aspectj.runtime.reflect.JoinPointImpl.proceed(JoinPointImpl.java:167)
at de.aspectjtest.ExampleAspect.aroundStuff(ExampleAspect.java:27)
at de.test.MainWithError.main(MainWithError.java:15)当我使用OpenJDK 11执行它时(当将一切设置为Java 8时,它工作得很好)。从FinalFieldConstructorExample中删除最终修饰符和从切入点中移除&& noSet()时,效果很好,输出包含
=== Call: Integer java.lang.Integer.valueOf(int) method-call
MainWithoutError.java:5 call(Integer java.lang.Integer.valueOf(int))
call(public static java.lang.Integer java.lang.Integer.valueOf(int))
=== Call: Integer de.test.NonFinalFieldConstructorExample.parameters field-set
MainWithoutError.java:5 set(Integer de.test.NonFinalFieldConstructorExample.parameters)
set(private java.lang.Integer de.test.NonFinalFieldConstructorExample.parameters)因此,我认为对静态字段的设置调用(具有getKind of field-set (似乎不在OpenJDK 8中)是问题的原因。是否有任何方法将其排除在AspectJ工具之外(或解决问题)?文档(https://www.eclipse.org/aspectj/doc/released/progguide/semantics-pointcuts.html#primitive-pointcuts)声明get可以在切入点中使用,但是我没有找到一种指定final的方法,即使我添加了noSet,它似乎也被触摸了,并且出现了错误。
发布于 2020-10-31 07:06:25
我想你在打AspectJ问题#563709。错误消息是相同的,它在Java8上工作也是一样的,但不是11 (可能是9+)。
因此,作为目前的解决办法,您希望避免向构造函数提供建议。要么你把他们排除在外
@Around("notWithinAspect() && noSet() && !(execution(*.new(..)))")或者,考虑到您的建议只在proceed()之前做一些事情,只需更改通知类型:
@Before("notWithinAspect() && noSet()")
public void beforeStuff(final JoinPoint thisJoinPoint, final EnclosingStaticPart thisEnclosingJoinPoint) {
System.out.println("=== Call: " + thisJoinPoint.getSignature() + " " + thisJoinPoint.getKind());
System.out.println(thisJoinPoint.getSourceLocation() + " " + thisJoinPoint.getStaticPart());
System.out.println(thisJoinPoint.toLongString());
}如果出于任何原因,您需要@Around,并且通常不能将其重构为@Before + @After对,则可以保留它而不执行构造函数,只为构造函数添加一个单独的@Before + @After通知对。
更新:
排除构造函数或只使用
@Before工作,但不能用于我的用例(方法执行持续时间监视)
那么这个解决方案如何呢?全球范围内用@Around + @Before + @After代替?您甚至可能会注意到,您的日志现在还显示了额外的preinitialization和initialization切入点,因为对于这些切入点类型,不支持它。这是我的MCVE
package de.scrum_master.app;
public class FinalFieldConstructorExample {
private final Integer parameters = 5;
public Integer getParameters() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {}
return parameters;
}
}package de.scrum_master.app;
public class MainWithError {
public static void main(String[] args) {
FinalFieldConstructorExample example = new FinalFieldConstructorExample();
System.out.println(example.getParameters());
}
}package de.scrum_master.aspect;
import java.util.Stack;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class MyAspect {
private ThreadLocal<Stack<Long>> startTimeTL = ThreadLocal.withInitial(() -> new Stack<>());
@Pointcut("within(de.scrum_master.aspect..*)")
public void withinAspect() {}
@Before("!withinAspect()")
public void beforeStuff(final JoinPoint thisJoinPoint) {
startTimeTL.get().push(System.currentTimeMillis());
}
@After("!withinAspect()")
public void afterStuff(final JoinPoint thisJoinPoint) {
System.out.println(thisJoinPoint + " -> " + (System.currentTimeMillis() - startTimeTL.get().pop()));
}
}控制台日志如下所示:
staticinitialization(de.scrum_master.app.MainWithError.<clinit>) -> 1
staticinitialization(de.scrum_master.app.FinalFieldConstructorExample.<clinit>) -> 0
preinitialization(de.scrum_master.app.FinalFieldConstructorExample()) -> 0
call(Integer java.lang.Integer.valueOf(int)) -> 0
set(Integer de.scrum_master.app.FinalFieldConstructorExample.parameters) -> 0
execution(de.scrum_master.app.FinalFieldConstructorExample()) -> 1
initialization(de.scrum_master.app.FinalFieldConstructorExample()) -> 1
call(de.scrum_master.app.FinalFieldConstructorExample()) -> 2
get(PrintStream java.lang.System.out) -> 0
call(void java.lang.Thread.sleep(long)) -> 100
get(Integer de.scrum_master.app.FinalFieldConstructorExample.parameters) -> 0
execution(Integer de.scrum_master.app.FinalFieldConstructorExample.getParameters()) -> 100
call(Integer de.scrum_master.app.FinalFieldConstructorExample.getParameters()) -> 100
5
call(void java.io.PrintStream.println(Object)) -> 1
execution(void de.scrum_master.app.MainWithError.main(String[])) -> 103P.S.:您是否知道,对于编织方法和构造函数,您同时记录相同方法/构造函数的call和execution?
https://stackoverflow.com/questions/64611859
复制相似问题