У меня есть некоторый код, который отлично работает без аннотации @Async, но, похоже, не работает с NPE внутри ссылки на @Autowired DAO, когда в методе присутствует @Async.
Вот определение свойств @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) Вызывается код AutoDired lockDAO, который, в свою очередь, пытается извлечь идентификатор пользователя, вошедшего в систему в данный момент, используя код ниже, 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, все методы FlexContext возвращают нуль после того, как не асинхронный метод, который порождает поток Async, вернулся.
Так как это можно решить. Мои методы блокировки извлекали информацию о пользователе на основе вызова FlexContext.getUserPrinciple. Сначала я думал, что проблема будет решена, сказав Spring распространять контекст безопасности дочерним потокам, как описано здесь: Наследовать контекст безопасности Spring в дочерних потоках , но это не улучшило ситуацию.
Решение оказалось проще, чем я думал, реализовав изменения для распространения контекста безопасности в дочерние потоки, мне просто нужно было изменить метод getCurrentUser () на:
public User getCurrentUser() {
return (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}