Java 在方法上使用@Async注释时,来自CglibAopProxy的NPE

Java 在方法上使用@Async注释时,来自CglibAopProxy的NPE,java,spring,asynchronous,Java,Spring,Asynchronous,当我尝试使用@Async注释发送电子邮件的方法时,我得到了NPE @Async @Override public void sendCloseShiftInfoFromText(Double cashBox, Double cache, Double bankKart, Double payWithCard, Double allPrice, Collection<? extends User> users

当我尝试使用@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);
}
我试图找到NPE的来源。到达字符串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();
    ...
}
从行列表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.setCurrentProxyoldProxy;这就是NPE的来源。 下面是调试器的屏幕截图

代码在没有注释的情况下运行良好


如果有人能解释为什么最后一段代码是这样工作的,我将不胜感激。找到解决方案也很好。

M.迪纳姆谢谢你的建议。事实证明,调试器有误导性。我通过打印到控制台找到了给出异常的实际方法;此方法正在调用Spring SecurityContext。由于该方法标记为@Async,因此它正在新线程中运行,并且上下文未经过身份验证。
解决方案是将SecurityContextHolder策略从默认模式\u THREADLOCAL更改为模式\u INHERITABLETHREADLOCAL。

不管调试器告诉您什么,我怀疑它是否真的会这样做。确保您正在查看正确版本代码的正确来源。我怀疑问题是由于调用了一些final方法或非public方法导致了对代理的调用,而不是通过代理调用实际的类。
    @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);
            }
        }
    }