当我尝试对发送电子邮件的方法使用 @Async 注释时,我收到了 NPE。
@Async
@Override
public void sendCloseShiftInfoFromText(Double cashBox, Double cache, Double bankKart, Double payWithCard,
Double allPrice, Collection<? extends User> users, Double shortage) {
MimeMessagePreparator[] mimeMessages = new MimeMessagePreparator[users.size()];
int messageNum = 0;
for (User user : users) {
String email = user.getEmail();
if (email == null) {
continue;
}
mimeMessages[messageNum++] = mimeMessage -> {
MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage);
messageHelper.setFrom(properties.getMail().getSender());
messageHelper.setTo(email);
messageHelper.setSubject(closeShiftSubject);
String html = htmlService.getCloseShiftFromText(closeShiftText, cashBox, cache, bankKart, payWithCard,
allPrice, closeShiftView, users, shortage);
messageHelper.setText(html, true);
};
}
if (messageNum == 0) {
return;
}
javaMailSender.send(mimeMessages);
}
这是我遇到的异常:
20:24:57.565 [SimpleAsyncTaskExecutor-1] ERROR org.springframework.web -
Asynchronous Exception :Could not prepare mail; nested exception is java.lang.NullPointerException
20:24:57.565 [SimpleAsyncTaskExecutor-1] ERROR org.springframework.web - Method name :sendCloseShiftInfoFromText
20:24:57.565 [SimpleAsyncTaskExecutor-1] ERROR org.springframework.web - Parameter :121246.0
20:24:57.566 [SimpleAsyncTaskExecutor-1] ERROR org.springframework.web - Parameter :0.0
20:24:57.566 [SimpleAsyncTaskExecutor-1] ERROR org.springframework.web - Parameter :0.0
20:24:57.566 [SimpleAsyncTaskExecutor-1] ERROR org.springframework.web - Parameter :0.0
20:24:57.566 [SimpleAsyncTaskExecutor-1] ERROR org.springframework.web - Parameter :0.0
20:24:57.566 [SimpleAsyncTaskExecutor-1] ERROR org.springframework.web - Parameter :[com.cafe.crm.models.user.User@4727bacf]
20:24:57.566 [SimpleAsyncTaskExecutor-1] ERROR org.springframework.web - Parameter :121246.0
我试图找到NPE的来源。到达“String html = htmlService.getCloseShiftFromText...
”后,调用以下方法:
@Override
public String getCloseShiftFromText(String text, Double cashBox, Double cache, Double bankKart, Double payWithCard,
Double allPrice, String view, Collection<? extends User> recipients,
Double shortage) {
List<User> usersOnShift = shiftService.getUsersOnShift();
...
}
来自行List<User> usersOnShift = shiftService.getUsersOnShift();
调试器转到 CglibAopProxy 类:
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Class<?> targetClass = null;
Object target = null;
try {
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// May be null. Get as late as possible to minimize the time we
// "own" the target, in case it comes from a pool...
target = getTarget();
if (target != null) {
targetClass = target.getClass();
}
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target.
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// We can skip creating a MethodInvocation: just invoke the target directly.
// Note that the final invoker must be an InvokerInterceptor, so we know
// it does nothing but a reflective operation on the target, and no hot
// swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse);
}
else {
// We need to create a method invocation...
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null) {
releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
尽管如此setProxyContext == false
调试器转到 AopContext.setCurrentProxy(oldProxy);
线,这就是 NPE 的来源。
下面是调试器的屏幕截图
代码在没有注释的情况下工作正常。
如果有人能解释为什么最后一段代码会这样工作,我将非常感激。如果能找到解决方案就好了。
最佳答案
M。 Deinum 感谢您的建议。事实证明,调试器不知何故产生了误导。我找到了通过打印到控制台给出异常的实际方法;该方法正在调用 Spring SecurityContext。由于该方法被标记为 @Async,因此它在新线程中运行,并且上下文未经过身份验证。 解决方案是将 SecurityContextHolder 策略从默认的 MODE_THREADLOCAL 更改为 MODE_INHERITABLETHREADLOCAL。
关于java - 在方法上使用 @Async 注释时来自 CglibAopProxy 的 NPE,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46746289/