我有一些代码在没有@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,n.b.我使用Spring Security进行身份验证和授权。
@Override
public User getCurrentUser()
{
String name = FlexContext.getUserPrincipal().getName();
User user = userDAO.findByName(name);
return user;
}
3) 因此,在上面的代码中,我发现FlexContext.getUserPrincipal()返回了一个null,然后导致InvocationTargetException。尽管出于某种原因,我不理解任何关于此InvocationTargetException的内容被写入日志或控制台。
因此,似乎是因为该方法被调用为@Async,所以在生成Async线程的非Async方法返回后,FlexContext的所有方法都返回了null。
那么如何解决这个问题呢。我的锁定方法是基于对FlexContext.getUserPrinciple的调用来获取用户详细信息。我最初认为,通过告诉Spring将安全上下文传播到子线程,可以解决这个问题,如下所述:在子线程中继承Spring安全上下文,但这并没有改善这种情况。
解决方案比我想象的要简单,实现了将安全上下文传播到子线程的更改后,我只需要将getCurrentUser()方法更改为:
public User getCurrentUser() {
return (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
不过,在这个时间点回答将是旧的,只需进行以下检查可能会有所帮助@EnableAsync在您的@Configuration中。还要确保方法"convertAttachment"调用应该在其他bean的外部。