/*我们使用Aspect在一些现有的应用程序上进行AOP,我们还使用threadlocal来存储GUId。我们使用@Around注释。在事务开始时,我们用initialValue()方法设置事务中的GUID。
问题是,当我们使用threadlocal时,我们还应该注意从threadlocale中删除数据,否则可能会导致内存执行失败。如果我在方面的最后删除它,它会破坏代码并更改UUID值。
请建议我们如何在不占用内存的情况下实现它。
法典:-*/
@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;
}
}
在开始之前,我们先给出一个提示:如果您编写@Around(“logging()”)
,那么您的切入点方法应该从loggingResponseTime()
重命名为实际上的0.05。
现在,至于你真正的问题:你犯了一个典型的初学者的错误,建议代码太广泛,也就是说,你拦截了所有的方法执行(在JDK之外)。如果您使用Eclipse和AJDT,并将光标放在< code>tracing()
建议上,您将在AspectJ“交叉引用”窗口中看到类似这样的内容,使用您当前的切入点:
您可以立即看到您的问题:您的切入点捕获匿名ThreadLocal
子类中的代码。这会导致无休止的递归,最终导致StackOverflow Error
,如果您检查它,您可以在自己的调用堆栈中看到。
下面是一些演示该问题的示例代码,供其他人参考:
驱动程序应用:
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";
}
}
方面:
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;
}
}
控制台输出:
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)
(...)
那么,您能做些什么呢?这实际上非常简单:只需从切入点中排除您不想拦截的连接点即可。为此,您有几种选择。我只是举几个例子:
A) 将你的方面放入一个特定的包中,并排除该包中的所有(方面)类:
@Pointcut("execution(* *(..)) && !within(de.scrum_master.aspect..*)")
B) 排除由@Aspect
注释的所有类:
@Pointcut("execution(* *(..)) && !within(@org.aspectj.lang.annotation.Aspect *)")
C) 排除与特定命名方案匹配的所有(方面)类,如*方面
:
@Pointcut("execution(* *(..)) && !within(*..*Aspect)")
d)从所有< code>ThreadLocal子类中排除代码(< code> 语法):
@Pointcut("execution(* *(..)) && !within(ThreadLocal+)")
在每种情况下,结果都是相同的:
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[])
顺便说一句:我对您使用UUID非常怀疑,因为我认为在这里创建昂贵的对象没有任何价值。只记录时间戳怎么样?为什么日志记录需要全局唯一的ID?他们什么也没告诉你。此外,如果您使用未注释的
ID,那么您不仅要为每个线程创建一个ID。remove()
您甚至可以为每个调用创建一个!对不起,这是膨胀的,它减慢了您的代码并创建了许多不必要的对象。我认为这不明智。
更新:
我忘了解释无休止的递归的原因:你的建议调用
ThreadLocal.get()
,假设它可能为空。实际上它不能,因为如果值尚未初始化,get()
通过利用初始值()
来执行此操作。即使您手动调用 remove(),
下次调用 get()
时,它也会再次初始化该值,依此类推。这是记录在案的行为:
返回此线程局部变量的当前线程副本中的值。如果变量没有当前线程的值,则首先将其初始化为调用
initialValue()
方法返回的值。
那么,一步一步地会发生什么呢?
< li >调用一个方法。 < li >你的建议生效了。 < li >您从建议中调用< code>id.get()
。 < Li > < code > thread local . get()检查是否设置了值,注意到没有值,并调用您重写的< code>initialValue()方法。 < li >因为被覆盖的< code>initialValue()方法是由match-all切入点< code >执行(* *(..)),您的建议在初始值设定之前再次生效。最终的结果是循环再次开始,如此循环往复——无止境的递归,quod erat demonstrandum。
因此,实际上,您的问题归结为从一个通知中对一个未初始化的ThreadLocal
子类调用get()
>,同时使用相同的通知来获取其用户定义的
我的建议是从切入点中排除您的方面,请参阅上面的切入点示例。您还应该去掉null
检查ThreadLocal
value,因为它是多余的。最后,我假设每个线程需要一个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";
}
}
改进方面:
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;
}
}
控制台输出:
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[])
正如您所看到的,线程已经有了唯一的ID,所以您可能需要考虑在没有任何UUID的情况下实现您的方面。
我想将我的VBA代码实现到SAS代码中,这样一次运行就可以完成整个过程。我的SAS代码读取一个大SAS表,进行一些转换,最后导出到Excel文件(代码如下)。我还在Excel文件中编写了一些VBA代码(例如,对某些变量进行自动筛选,您可以看到下面的代码)。 这张桌子看起来像这样: 然而,我想将我的VBA代码实现到SAS代码中,这样我就可以一次运行完成整个过程。我知道如何在SAS中打开和运行Exce
了解如何在“代码”视图中工作并充分利用 Dreamweaver 的编码功能。 可通过多种方式在 Dreamweaver 中处理代码。 您可以使用“新建文档”对话框打开新的代码文件,然后开始键入您的代码。在 Dreamweaver 中创建新的代码文件 键入时,会显示代码提示以帮助您选择代码和避免打字错误。如果需要,可使用 Dreamweaver 的有用的快捷文档获取 CSS 的相关帮助。 还可以使用
我试图使用VS Code,但我有一个问题,打开与其他编辑器编写的代码,VS Code不能阅读韩语Unicode?UTF-8?我们在代码中称其他语言为什么? 我用vim编辑器编写了代码,其中一些注释是用韩语编写的,任何其他编辑器都可以阅读韩语,但VS代码如下所示。 我怎样才能解决这个问题?
问题内容: 我可以很轻松地在Node.js中编写非阻塞I / O。 这就是整个库的用途。 但是所做的任何计算都是阻塞的。任何通过事件发送器的消息都将被阻止。 例如,发出事件立即得到解决,因此被阻止: 除了将调用包装起来外,如何使代码无阻塞? 我希望在事件循环的每个周期中进行尽可能少的计算,以便可以同时为尽可能多的客户端提供服务。 如何以非阻塞方式编写代码? 当我拥有非阻塞代码时,如何在多个进程之间
问题内容: 我想评估 作为一个块,而不是逐行评估 有没有简单的方法可以将提示移至下一行? 问题答案: 节点v6.4具有一种模式。在repl提示符下,您可以输入多行。 例 以下是所有特殊repl命令的文档 https://nodejs.org/api/repl.html#repl_commands_and_special_keys
11.3 编写代码 要完成我们的程序,我们需要创建一个Java文件。默认情况下,Maven会编译src/main/java目录下的源文件,因此您需要创建该目录结构,然后添加一个名为src/main/java/Example.java的文件: import org.springframework.boot.*; import org.springframework.boot.autoconfigur