我有一个Spring批处理应用程序,我们在其中使用SimpleJobLauncher。那样,我们有Rest请求在excel上触发批处理作业,该作业在保存编写者阶段的实体时,不会通过收到的HTTP线程选择AuditorAware最新设置。
我尝试在JobParameter中设置用户名,然后在writer中获取该用户名以设置用户名线程局部变量,但这也没有被选择。
一旦我们收到向审计员提供用户信息的请求,我就使用Threadlocal来存储用户。
public static final InheritableThreadLocal<String> USERNAME = new InheritableThreadLocal<>();
@Bean
public AuditorAware<String> auditorProvider() {
if (USERNAME.get() != null) {
return () -> Optional.of(USERNAME.get());
}
return () -> Optional.of(WebConstant.DEFAULT_USER);
}
public class HeaderInterceptor implements HandlerInterceptor {
/**
* This implementation always returns {@code true}.
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String username = request.getHeader("username");
if(null == username) {
throw new UserNotProvidedException("USER_NOT_PROVIDED");
}
USERNAME.set(username);
return true;
}
}
答案 0 :(得分:1)
AuditorAware由Spring应用程序事件调用,如果您为事件多播程序指定了任务执行程序,则解决此问题的方法是创建一个自定义的AuditingEventListener,在同一线程内调用AuditingEventListener
@Bean(name = "applicationEventMulticaster")
public ApplicationEventMulticaster simpleApplicationEventMulticaster() {
SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster() {
private ResolvableType resolveDefaultEventType(ApplicationEvent event) {
return ResolvableType.forInstance(event);
}
@Override
public void multicastEvent(ApplicationEvent event, ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (listener instanceof AuditingEventListener) {
invokeListener(listener, event);
return;
}
executor.execute(() -> invokeListener(listener, event));
}
}
};
SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor("s-event-");
multicaster.setTaskExecutor(executor);
return multicaster;
}
答案 1 :(得分:0)
如果我正确理解了您的问题,则可以将用户名放在一个线程的ThreadLocal
中,然后尝试从另一个线程访问它。
从ThreadLocal
的文档中可以明显看出这是行不通的。
每个访问一个线程(通过其get或set方法)的线程都有其自己的,独立初始化的变量副本。
您需要通过其他方式将用户名传递给批处理作业。
我从未使用过Spring Boot,但是this article描述了JobContext
及其使用方法。
我不确定您是否可以将其中任何一个注入AuditorAware
中。
另外,您也可以在设置中以单个ThreadLocal
运行的所有内容的开头,将值放在Thread
中。