AuditorAware没有为Spring Batch线程提供最新价值

时间:2019-07-09 13:21:20

标签: java spring spring-data-jpa spring-batch

我有一个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;
    }
}

2 个答案:

答案 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中。