Java 如何在主线程下拦截可运行的创建和执行,以使用AspectJ填充上下文流数据

Java 如何在主线程下拦截可运行的创建和执行,以使用AspectJ填充上下文流数据,java,multithreading,aop,aspectj,mdc,Java,Multithreading,Aop,Aspectj,Mdc,原版 将Java MDC从一个线程填充到其生成的所有内部线程(父到子关系) 使用AspectJ的在制品解决方案 我可以编写一个方面来拦截所有可运行的创建,但由于每次使用都需要一个不同的方面实例(带有自定义注释),因此在从父线程执行代码时,我必须将MDC存储在某个地方,我无法编写切入点截取新创建的Runnable实例,因此我可以使用前面的上下文映射设置MDC 这是一个方面 @Aspect(“percflow(@annotation(com.bell.cts.commons.cron.framewo

原版

将Java MDC从一个线程填充到其生成的所有内部线程(父到子关系)

使用AspectJ的在制品解决方案

我可以编写一个方面来拦截所有
可运行的
创建,但由于每次使用都需要一个不同的方面实例(带有自定义注释),因此在从父线程执行代码时,我必须将MDC存储在某个地方,我无法编写切入点截取新创建的
Runnable
实例,因此我可以使用前面的上下文映射设置MDC

这是一个方面

@Aspect(“percflow(@annotation(com.bell.cts.commons.cron.framework.scheduler.domain.MDCTrace)))
公共类MDCTraceAspect{
私有最终记录器Logger=LoggerFactory.getLogger(MDCTraceAspect.class);
私人互联网i;
私有最终地图contextMap;
公共MDCTraceAspect(){
i=新随机().nextInt();
MDC.clear();
MDC.put(“IP”,String.valueOf(i));
contextMap=MDC.getCopyOfContextMap();
debug(String.format(“[%d]新特性”,Thread.currentThread().getId());
}
@之前(“执行(可运行+.new(..)”)
新建之前的公共无效可运行(连接点连接点){
setContextMap(contextMap);
debug(String.format(“[%d]New Runnable”,Thread.currentThread().getId());
}
@在(“执行(*Runnable+.*(..))之前)
前公共无效(连接点连接点){
setContextMap(contextMap);
logger.info(String.format(“[%d]RUNNABLE WORKS!”,Thread.currentThread().getId());
}
@之前(“执行(void Child.run())”)
ChildRun之前的公共无效(JoinPoint JoinPoint){
setContextMap(contextMap);
logger.info(String.format(“[%d]子工作!”,Thread.currentThread().getId());
}
}
这里有一个
父项
子项
和自定义注释

公共类父类{
私有最终记录器=LoggerFactory.getLogger(父类);
私人遗嘱执行人服务;
@MDCTrace
public void runmulti-threadbyexecutor()引发InterruptedException{
executorService=Executors.newCachedThreadPool();
logger.info(String.format(“[%d]在启动子线程之前)”,thread.currentThread().getId());
executorService.submit(新的子项());
logger.info(String.format(“[%d]在启动子线程之后”,thread.currentThread().getId());
List.of(10,11,12,13,14).parallelStream().forEach(i->{
logger.info(String.format(“[%d]循环迭代#%d”,Thread.currentThread().getId(),i));
});
executorService.shutdown();
执行器服务。等待终止(1,时间单位。秒);
logger.info(String.format(“[%d]ExecutorService已结束”,Thread.currentThread().getId());
}
公共静态void main(字符串[]args)引发InterruptedException{
父项=新父项();
parent.runmulti-threadbyexecutor();
}
}
公共类子类实现可运行{
私有最终记录器=LoggerFactory.getLogger(Child.class);
@凌驾
公开募捐{
logger.info(String.format(“[%d]在子线程中运行”,thread.currentThread().getId());
}
}
@Retention(RetentionPolicy.RUNTIME)
@目标(ElementType.METHOD)
public@interface-MDCTrace{
}
目标

最后一个目标是只需注释MDC上下文的入口点,就可以拦截在执行注释方法(甚至其他对象)时创建的任何线程/可运行程序/未来,从而使用存储在当前上下文流的方面实例中的原始/父线程MDC信息正确设置MDC

在儿童跑步之前的
和在儿童跑步之前的
两个选项都不起作用,我找不到如何使其中一个起作用

多谢各位


奖励点如果有人可以指导我如何使这也适用于
并行流

首先,您需要了解新线程不在其父线程的控制流内。有关示例代码和控制台日志的解释,请参见我的其他答案:

因此,正如您已经注意到的,与
cflow()
或方面实例化
percflow()
相关的任何内容都不会在这种情况下工作

获得部分所需内容的唯一方法是手动簿记,如果您使用编译时编织,则至少对于您自己的类,如果您使用加载时编织,则对于第三方JAR/类(JRE类除外)

看看这个例子,我对您自己的代码进行了一些修改,以显示一种变通方法及其限制。我还想避免使用任何日志框架,而是打印到
System.out
。因此,为了使代码能够编译,我不得不用一个伪类替换
MDC

package de.scrum\u master.app;
导入java.util.HashMap;
导入java.util.Map;
公共类MDC{
private static ThreadLocal contextMap=new InheritableThreadLocal();
静止的{
清除();
}
公共静态无效清除(){
set(新的HashMap());
}
公共静态void put(字符串键、字符串值){
contextMap.get().put(键,值);
}
公共静态映射getCopyOfContextMap(){
返回新的HashMap(contextMap.get());
}
公共静态void setContextMap(映射contextMap){
MDC.contextMap.set(contextMap);
}
}
package de.scrum\u master.app;
导入java.lang.annotation.ElementType;
导入java.lang.annotation.Retention;
导入java.lang.annotation.RetentionPolicy;
导入java.lang.annotation.Target;
@保留(RetentionPolicy.RUNTIME)
@目标(ElementType.METHOD)
public@interface MDCTrace{}
package de.scrum\u master.app;
公共类子级实现可运行{
@凌驾
公开募捐{
System.out.println(String.format([%d]在子线程中运行),thread.currentThread().getId());
}
}