您可以在 @Async 方法中使用 @Autowired 属性吗

发布于 2024-11-16 09:38:59 字数 3527 浏览 1 评论 0原文

我有一些代码在没有 @Async 注释的情况下可以正常工作,但当方法上存在 @Async 时,在对 @Autowired DAO 的引用中出现 NPE 似乎会失败。

这是 @Autowired 属性的定义

@Component
public class DocumentManager implements IDocumentManager {

    private Log logger = LogFactory.getLog(this.getClass());
    private OfficeManager officeManager = null;

    @Autowired
    private IConverterService converterService;

    @Autowired
    private IApplicationProperties applicationProperties;

    @Autowired
    private ILockDAO lockDAO;

    @Autowired
    private IFileAttachmentDAO fileAttachmentDAO;

这是失败的方法(在同一个类中)。

@Async
public void convertAttachment(Integer id) {
  // Now because we are running asynchronously it is likely that the caller
  // to the Create/Update still has a lockUID on this record.
  // We will need our own lockUID before we can update the PDF property

  Long retryInterval = 5000L; // 5 seconds
  Integer retryCount = 5;
  Integer attempts = 0;
  String lockUID = null;

  while (attempts < retryCount && UtilityClass.isNullOrEmpty(lockUID)) {
    attempts++;
    // Seems to go wrong here debugger shows
    ValidationResult result = lockDAO.create(EntityTypeEnum.FILE_ATTACHMENT, id);
    lockUID = lockDAO.getLockUIDFromResult(result);
    if (UtilityClass.isNullOrEmpty(lockUID)) {
      try {
        Thread.sleep(retryInterval);
      } catch (InterruptedException e) {
        // http://www.ibm.com/developerworks/java/library/j-jtp05236/index.html
        Thread.currentThread().interrupt();
      }
    }
  }

  if (!UtilityClass.isNullOrEmpty(lockUID)) {

这是堆栈跟踪中的一个片段:

Thread [myExecutor-1] (Suspended)   
InvocationTargetException.<init>(Throwable) line: 54    
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]  
NativeMethodAccessorImpl.invoke(Object, Object[]) line: 39  
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25  
Method.invoke(Object, Object...) line: 597  
AopUtils.invokeJoinpointUsingReflection(Object, Method, Object[]) line: 309 
ReflectiveMethodInvocation.invokeJoinpoint() line: 183  
ReflectiveMethodInvocation.proceed() line: 150  
AsyncExecutionInterceptor$1.call() line: 80 
FutureTask$Sync.innerRun() line: 303    
FutureTask<V>.run() line: 138   
ThreadPoolExecutor$Worker.runTask(Runnable) line: 886   
ThreadPoolExecutor$Worker.run() line: 908   
Thread.run() line: 619  

在评论的提示下进一步挖掘,我就这样做了,令我羞愧的是我发现了以下内容。

1)程序实际上并没有崩溃或抛出异常。

2)正在调用 Autowired lockDAO 代码,这反过来会尝试使用下面的代码检索当前登录的用户 ID,注意我正在使用 Spring Security 进行身份验证和授权。

@Override
public User getCurrentUser()
{
  String name = FlexContext.getUserPrincipal().getName();
  User user = userDAO.findByName(name);
  return user;
}

3)因此,在上面的代码中,我发现 FlexContext.getUserPrincipal() 返回一个 null,然后导致 InvokingTargetException。尽管由于某种原因,我不明白有关将此 InvocableTargetException 写入日志或控制台的任何信息。

因此,由于该方法被调用 @Async,因此在生成异步线程的非异步方法返回后,FlexContext 的所有方法都会返回 null。

那么如何解决这个问题呢。我的锁定方法是根据对 FlexContext.getUserPrinciple 的调用来获取用户详细信息。我最初认为问题可以通过告诉 Spring 将安全上下文传播到子线程来解决,如下所述: 在子线程中继承 Spring Security Context,但这并没有改善情况。

解决方案比我想象的更简单,实现了将安全上下文传播到子线程的更改,我只需将 getCurrentUser() 方法更改为:

public User getCurrentUser() {
  return (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}

I have some code that works fine without the @Async annotation but seems to fail with a NPE inside the reference to the @Autowired DAO when @Async is present on the method.

Here's the definition of the @Autowired properties

@Component
public class DocumentManager implements IDocumentManager {

    private Log logger = LogFactory.getLog(this.getClass());
    private OfficeManager officeManager = null;

    @Autowired
    private IConverterService converterService;

    @Autowired
    private IApplicationProperties applicationProperties;

    @Autowired
    private ILockDAO lockDAO;

    @Autowired
    private IFileAttachmentDAO fileAttachmentDAO;

And here is the method whic is failing (in the same class).

@Async
public void convertAttachment(Integer id) {
  // Now because we are running asynchronously it is likely that the caller
  // to the Create/Update still has a lockUID on this record.
  // We will need our own lockUID before we can update the PDF property

  Long retryInterval = 5000L; // 5 seconds
  Integer retryCount = 5;
  Integer attempts = 0;
  String lockUID = null;

  while (attempts < retryCount && UtilityClass.isNullOrEmpty(lockUID)) {
    attempts++;
    // Seems to go wrong here debugger shows
    ValidationResult result = lockDAO.create(EntityTypeEnum.FILE_ATTACHMENT, id);
    lockUID = lockDAO.getLockUIDFromResult(result);
    if (UtilityClass.isNullOrEmpty(lockUID)) {
      try {
        Thread.sleep(retryInterval);
      } catch (InterruptedException e) {
        // http://www.ibm.com/developerworks/java/library/j-jtp05236/index.html
        Thread.currentThread().interrupt();
      }
    }
  }

  if (!UtilityClass.isNullOrEmpty(lockUID)) {

Here is a snippet from the stack trace:

Thread [myExecutor-1] (Suspended)   
InvocationTargetException.<init>(Throwable) line: 54    
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]  
NativeMethodAccessorImpl.invoke(Object, Object[]) line: 39  
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25  
Method.invoke(Object, Object...) line: 597  
AopUtils.invokeJoinpointUsingReflection(Object, Method, Object[]) line: 309 
ReflectiveMethodInvocation.invokeJoinpoint() line: 183  
ReflectiveMethodInvocation.proceed() line: 150  
AsyncExecutionInterceptor$1.call() line: 80 
FutureTask$Sync.innerRun() line: 303    
FutureTask<V>.run() line: 138   
ThreadPoolExecutor$Worker.runTask(Runnable) line: 886   
ThreadPoolExecutor$Worker.run() line: 908   
Thread.run() line: 619  

Prompted by comments to dig further I did just that and to my shame I discovered the following.

1) The program is not actually crashing or throwing an exception.

2) The Autowired lockDAO code is being called and this in turn tries to retrieve the currently logged in user id using the code below, n.b. I am using Spring Security for authentication and Authorisation.

@Override
public User getCurrentUser()
{
  String name = FlexContext.getUserPrincipal().getName();
  User user = userDAO.findByName(name);
  return user;
}

3) So it is in the code above that I discovered that FlexContext.getUserPrincipal() is returning a null which then causes an InvocationTargetException. Although for some reason I do not understand nothing regarding this InvocationTargetException is written to the log or console.

So it appears that because the method is being called @Async then all the methods of FlexContext return a null after the non Async method that spawns the Async thread has returned.

So how can this be resolved. My locking methods were fetching the user details based on the call to FlexContext.getUserPrinciple. I had initially thought that the problem would be resolved by telling Spring to propagate the security context to child threads as explained here: Inherit Spring Security Context in Child Threads, but that did not improve the situation.

The solution was simpler than I thought, having implemented the changes to propagate the Security Context to child threads I just needed to change my getCurrentUser() method to:

public User getCurrentUser() {
  return (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

难理解 2024-11-23 09:38:59

不过,此时的回答已经过时了,只是进行一下检查,如果
@EnableAsync 在你的@Configuration 中。还要确保“convertAttachment”方法调用应该在其他 bean 的外部。

Answering at this point of time will be old though, just going through so following check could be helpful if
@EnableAsync in there in your @Configuration. Also make sure method 'convertAttachment' invocation should be outside in some other bean.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文