Vim*_*dav 0 java aop aspectj thread-local thread-local-storage
/*我们使用Aspect在一些现有应用程序上执行AOP,我们还使用threadlocal来存储GUId.我们正在使用@Around注释.在事务开始时,我们使用initialValue()方法在事务中设置GUID.
问题就像我们所知,当我们使用threadlocal时,我们也应该注意从threadlocal中删除数据,否则它可能导致我的内存执行.如果我在最后一个方面删除它,它会破坏代码并更改UUID值.
请建议我们如何在没有outofmemory的情况下实现它.
代码: - */
@Aspect
public class DemoAspect {
@Pointcut("execution(* *.*(..)) ")
public void logging() {}
private static ThreadLocal<String> id = new ThreadLocal<String>() {
@Override
protected String initialValue(){
return UUID.randomUUID().toString();
}
};
@Around("logging()")
public Object tracing(ProceedingJoinPoint thisJoinPoint) throws Throwable {
String methodSignature=thisJoinPoint.getSignature().toString();
if(id.get().toString()==null || id.get().toString().length()==0)
id.set(UUID.randomUUID().toString());
System.out.println("Entering into "+methodSignature);
Object ret = thisJoinPoint.proceed();
System.out.println(id.get().toString());
System.out.println("Exiting into "+methodSignature);
//id.remove();
return ret;
}
}
Run Code Online (Sandbox Code Playgroud)
在我们开始一点提示之前:如果你写了@Around("logging()")你的切入点方法应该从loggingResponseTime()实际重命名logging(),否则方面将无法工作.
至于你真正的问题:你通过过于宽泛地建议代码来制造典型的初学者错误,即你正在拦截所有方法执行(在JDK之外).如果您使用Eclipse和AJDT,并将光标放在tracing()建议中,您将在AspectJ"交叉引用"窗口中使用当前切入点看到类似的内容:

package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
System.out.println(bar(foo()));
}
public static String bar(String text) {
return text + "bar";
}
private static String foo() {
return "foo";
}
}
Run Code Online (Sandbox Code Playgroud)
方面:
package de.scrum_master.aspect;
import java.util.UUID;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class DemoAspect {
private static ThreadLocal<String> id = new ThreadLocal<String>() {
@Override
protected String initialValue() {
return UUID.randomUUID().toString();
}
};
@Pointcut("execution(* *(..))")
public void logging() {}
@Around("logging()")
public Object tracing(ProceedingJoinPoint thisJoinPoint) throws Throwable {
String methodSignature = thisJoinPoint.getSignature().toString();
if (id.get().toString() == null || id.get().toString().length() == 0)
id.set(UUID.randomUUID().toString());
System.out.println("Entering into " + methodSignature);
Object ret = thisJoinPoint.proceed();
System.out.println(id.get().toString());
System.out.println("Exiting from " + methodSignature);
id.remove();
return ret;
}
}
Run Code Online (Sandbox Code Playgroud)
控制台输出:
Exception in thread "main" java.lang.StackOverflowError
at org.aspectj.runtime.reflect.SignatureImpl$CacheImpl.get(SignatureImpl.java:217)
at org.aspectj.runtime.reflect.SignatureImpl.toString(SignatureImpl.java:50)
at org.aspectj.runtime.reflect.SignatureImpl.toString(SignatureImpl.java:62)
at de.scrum_master.aspect.DemoAspect$1.initialValue_aroundBody1$advice(DemoAspect.aj:29)
at de.scrum_master.aspect.DemoAspect$1.initialValue(DemoAspect.aj:1)
at de.scrum_master.aspect.DemoAspect$1.initialValue(DemoAspect.aj:1)
at java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:160)
at java.lang.ThreadLocal.get(ThreadLocal.java:150)
at de.scrum_master.aspect.DemoAspect$1.initialValue_aroundBody1$advice(DemoAspect.aj:30)
at de.scrum_master.aspect.DemoAspect$1.initialValue(DemoAspect.aj:1)
at de.scrum_master.aspect.DemoAspect$1.initialValue(DemoAspect.aj:1)
at java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:160)
at java.lang.ThreadLocal.get(ThreadLocal.java:150)
(...)
Run Code Online (Sandbox Code Playgroud)
所以,你可以做什么?它实际上非常简单:只需从切入点中排除您不想真正拦截的连接点.为此你有几个选择.我只是命名一些:
A)将您的方面放入特定包中并排除该包中的所有(方面)类:
@Pointcut("execution(* *(..)) && !within(de.scrum_master.aspect..*)")
Run Code Online (Sandbox Code Playgroud)
B)排除所有注释的类@Aspect:
@Pointcut("execution(* *(..)) && !within(@org.aspectj.lang.annotation.Aspect *)")
Run Code Online (Sandbox Code Playgroud)
C)排除匹配某个命名方案的所有(方面)类,如*Aspect:
@Pointcut("execution(* *(..)) && !within(*..*Aspect)")
Run Code Online (Sandbox Code Playgroud)
D)从所有ThreadLocal子类中排除代码(+语法):
@Pointcut("execution(* *(..)) && !within(ThreadLocal+)")
Run Code Online (Sandbox Code Playgroud)
在每种情况下,结果都是相同的:
Entering into void de.scrum_master.app.Application.main(String[])
Entering into String de.scrum_master.app.Application.foo()
d2b83f5f-7282-4c06-9b81-6601c8e0499d
Exiting from String de.scrum_master.app.Application.foo()
Entering into String de.scrum_master.app.Application.bar(String)
0d1c9463-4bbd-427d-9d64-c7f3967756cf
Exiting from String de.scrum_master.app.Application.bar(String)
foobar
aa96bbbd-a1a1-450f-ae6e-77ab204c5fb2
Exiting from void de.scrum_master.app.Application.main(String[])
Run Code Online (Sandbox Code Playgroud)
顺便说一下:我对你对UUIDs 的使用有很强的怀疑,因为我认为在这里创建昂贵的对象毫无价值.如何记录时间戳?为什么需要全局唯一ID进行日志记录?他们什么也没告诉你.此外,您不仅要为每个线程创建一个ID,而且如果您使用未注释的,id.remove()您甚至可以为每个呼叫创建一个ID !对不起,但这很臃肿,它会降低代码速度并产生大量不必要的对象.我不认为这是明智的.
更新:
我忘了解释无限递归的原因:你的建议调用ThreadLocal.get(),假设它可能为空.实际上它不可能是因为如果价值尚未初始化,get()则通过利用来实现initialValue().即使您手动呼叫remove(),下次调用get()它时也会再次初始化该值,依此类推.这是记录在案的行为:
返回当前线程的此线程局部变量副本中的值.如果变量没有当前线程的值,则首先将其初始化为调用
initialValue()方法返回的值.
那么,一步一步地发生了什么呢?
id.get()从建议中打来电话.ThreadLocal.get()检查是否设置了值,注意到没有值并调用被覆盖的initialValue()方法.initialValue()匹配所有切入点捕获了重写方法execution(* *(..)),所以在设置初始值之前,您的建议再次启动.最终的结果是循环再次开始 - 等等 - 无休止的递归,quod erat demonstrandum.所以实际上你的问题归结为从一个建议中调用get()一个未初始化的ThreadLocal子类,同时initialValue()用相同的建议来定位它的用户定义的方法.这就是创建无限递归并最终使您的堆栈溢出的原因.
我的建议是从切入点中排除您的方面,请参阅上面的示例切入点.您还应该删除对值的null检查,ThreadLocal因为它是多余的.最后但并非最不重要的,我假设你想要ThreadLocal每个线程一个值而不是每个方法调用一个值.所以你可以不用任何一个set()或remove()完全打电话.
修改驱动程序类,创建一个额外的线程:
package de.scrum_master.app;
public class Application {
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(bar(foo()));
}
}).start();
Thread.sleep(200);
}
public static String bar(String text) {
return text + "bar";
}
private static String foo() {
return "foo";
}
}
Run Code Online (Sandbox Code Playgroud)
改进方面:
package de.scrum_master.aspect;
import java.util.UUID;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class DemoAspect {
private static ThreadLocal<UUID> id = new ThreadLocal<UUID>() {
@Override
protected UUID initialValue() {
return UUID.randomUUID();
}
};
@Pointcut("execution(* *(..)) && !within(DemoAspect)")
public void logging() {}
@Around("logging()")
public Object tracing(ProceedingJoinPoint thisJoinPoint) throws Throwable {
Signature methodSignature = thisJoinPoint.getSignature();
System.out.println(
"Thread " + Thread.currentThread().getId() +
"[" + id.get() +
"] >>> " + methodSignature
);
Object result = thisJoinPoint.proceed();
System.out.println(
"Thread " + Thread.currentThread().getId() +
"[" + id.get() +
"] <<< " + methodSignature
);
return result;
}
}
Run Code Online (Sandbox Code Playgroud)
控制台输出:
Thread 1[549d0856-0a92-4031-9331-a1317d6a43c4] >>> void de.scrum_master.app.Application.main(String[])
Thread 9[32c8444c-0f1f-4023-9b97-69d5beda3b4c] >>> void de.scrum_master.app.Application.1.run()
Thread 9[32c8444c-0f1f-4023-9b97-69d5beda3b4c] >>> String de.scrum_master.app.Application.access$0()
Thread 9[32c8444c-0f1f-4023-9b97-69d5beda3b4c] >>> String de.scrum_master.app.Application.foo()
Thread 9[32c8444c-0f1f-4023-9b97-69d5beda3b4c] <<< String de.scrum_master.app.Application.foo()
Thread 9[32c8444c-0f1f-4023-9b97-69d5beda3b4c] <<< String de.scrum_master.app.Application.access$0()
Thread 9[32c8444c-0f1f-4023-9b97-69d5beda3b4c] >>> String de.scrum_master.app.Application.bar(String)
Thread 9[32c8444c-0f1f-4023-9b97-69d5beda3b4c] <<< String de.scrum_master.app.Application.bar(String)
foobar
Thread 9[32c8444c-0f1f-4023-9b97-69d5beda3b4c] <<< void de.scrum_master.app.Application.1.run()
Thread 1[549d0856-0a92-4031-9331-a1317d6a43c4] <<< void de.scrum_master.app.Application.main(String[])
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,线程已经拥有唯一的ID,因此您可能需要考虑在没有任何UUID的情况下实现您的方面.
| 归档时间: |
|
| 查看次数: |
1469 次 |
| 最近记录: |