【问题标题】:AuditorAware not providing latest value to Spring Batch threadsAuditorAware 未向 Spring Batch 线程提供最新值
【发布时间】:2019-07-09 13:21:20
【问题描述】:

我有一个使用 SimpleJobLauncher 的 Spring 批处理应用程序。在那,我们有 Rest 请求触发 excel 上的批处理作业,在写入器阶段保存实体时,不会通过收到的 HTTP 线程选择 AuditorAware 最新设置。

我尝试在 JobParameter 中设置用户名,然后在 writer 中获取相同的用户名以设置用户名线程局部变量,但也没有被选中。

一旦我们收到向auditoraware提供用户信息的请求,我就会使用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;
    }
}

【问题讨论】:

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


    【解决方案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;
        }
    

    【讨论】:

      【解决方案2】:

      如果我正确理解您的问题,您可以将用户名放在一个线程中的 ThreadLocal 中,然后尝试从另一个线程访问它。

      ThreadLocal 的文档中应该很明显,这是行不通的。

      每个访问一个线程(通过它的 get 或 set 方法)都有自己的、独立初始化的变量副本。

      您需要通过其他方式将用户名传递给批处理作业。 我从未使用过 Spring Boot,但this article 描述了JobContext 以及如何使用它。

      我不确定您是否可以将其中任何一个注入您的AuditorAware。 或者,您可以将值放在 ThreadLocal 的开头,以在您的设置中以单个 Thread 运行。

      【讨论】:

      • @Jean Schauder,即使我也有同样的理解,为什么我尝试在 Spring 批处理的编写器线程中设置用户名,但是当我在我们的实体上使用下面的更改时,事情变得更奇怪了。 @PrePersist public void prePersist() { String createdByUser = WebConstant.USERNAME.get(); this.createdBy = createdByUser; this.updatedBy = createdByUser; } @PreUpdate public void preUpdate() { String updatedBy = WebConstant.USERNAME.get(); this.updatedBy = updatedBy; }
      • 使用上述代码,它开始工作与我的理解相反,即 InheritableThreadLocal 可以从父线程中获取值,但这里 Spring 批处理中的编写器线程能够获取值。
      猜你喜欢
      • 2015-02-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-31
      • 2015-10-05
      • 2015-03-06
      • 2013-05-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多