如何排查配置类中@Autowired注入XDDeliveryCallback接口为空问题
问题背景
我有如下配置类:
@Configuration public class XDDeliveryConfig { @Value("${direct.gateway.xd.endpointUrl: http://localhost:8080/xd/services/DocumentRepository_Service}") protected String endpointURL; @Autowired protected RoutingResolver resolver; @Autowired protected XDDeliveryCallback xdDeliveryCallback; @Bean @ConditionalOnMissingBean public XDDeliveryCore xdDeliveryCore() { final ReliableDispatchedNotificationProducer notificationProducer = new ReliableDispatchedNotificationProducer( new NotificationSettings(true, "Direct XD Delivery Agent", "Your message was successfully dispatched.")); return new XDDeliveryCore( resolver, xdDeliveryCallback, new DefaultTxDetailParser(), new DefaultMimeXdsTransformer(), new DocumentRepository(), notificationProducer, endpointURL); } }
XDDeliveryCallback接口定义:
public interface XDDeliveryCallback { void sendNotificationMessage(NotificationMessage message) throws MessagingException; }
XDDeliveryCore构造方法:
public class XDDeliveryCore { protected final RoutingResolver resolver; protected final XDDeliveryCallback callback; protected final TxDetailParser txParser; protected final MimeXdsTransformer mimeXDSTransformer; protected final DocumentRepository documentRepository; protected final NotificationProducer notificationProducer; protected final String endpointUrl; public XDDeliveryCore(RoutingResolver resolver, XDDeliveryCallback callback, TxDetailParser txParser, MimeXdsTransformer mimeXDSTransformer, DocumentRepository documentRepository, NotificationProducer notificationProducer, String endpointUrl) { this.resolver = resolver; this.callback = callback; this.txParser = txParser; this.mimeXDSTransformer = mimeXDSTransformer; this.endpointUrl = endpointUrl; this.documentRepository = documentRepository; this.notificationProducer = notificationProducer; } }
接口实现类:
@Configuration public class XDRemoteDeliveryProcessor implements XDDeliveryCallback { @Autowired protected XDDeliveryCore deliveryCore; @Autowired protected SmtpGatewayMessageSource smtpMessageSource; @Autowired protected DSNCreator dsnCreator; @Bean public Consumer<Message<?>> directXDDeliveryInput() { return streamMsg -> { try { final SMTPMailMessage smtpMessage = SMTPMailMessageConverter.fromStreamMessage(streamMsg); log.debug("XDRemoteDeliveryProcessor processing message from " + smtpMessage.getMailFrom().toString()); deliveryCore.processAndDeliverXDMessage(smtpMessage); } catch (MessagingException e) { throw new RuntimeException(e); } }; } public void sendNotificationMessage(NotificationMessage message) throws MessagingException { smtpMessageSource.sendMimeMessage(message); } }
目前发现XDDeliveryCore中的callback字段为null,需要排查并解决该问题。
核心原因:循环依赖导致注入失败
XDDeliveryConfig创建XDDeliveryCoreBean时,需要注入XDDeliveryCallback(即XDRemoteDeliveryProcessor);而XDRemoteDeliveryProcessor又通过@Autowired注入XDDeliveryCore,形成了循环依赖。Spring处理这种场景时,字段注入+工厂方法创建Bean的组合会导致初始化顺序混乱,XDDeliveryCallback还未完成Bean初始化就被注入到XDDeliveryCore,最终出现null值。
排查步骤
查看Spring上下文日志
- 开启Spring DEBUG级别日志,搜索
XDDeliveryCore和XDRemoteDeliveryProcessor的初始化日志,检查是否存在Circular reference detected相关警告或错误,直接确认循环依赖问题。
- 开启Spring DEBUG级别日志,搜索
验证组件扫描范围
- 确认
XDRemoteDeliveryProcessor所在的包被@ComponentScan覆盖,确保Spring能扫描到该类并创建对应的Bean。
- 确认
检查
@ConditionalOnMissingBean的影响- 确认没有其他地方提前创建了
XDDeliveryCore的Bean,导致XDDeliveryConfig中的xdDeliveryCore()方法未执行,而其他创建方式未正确注入XDDeliveryCallback。
- 确认没有其他地方提前创建了
解决方案
方案1:延迟加载打破循环依赖
将XDRemoteDeliveryProcessor中的字段注入改为构造器注入,并通过@Lazy延迟加载XDDeliveryCore,让Spring先完成XDRemoteDeliveryProcessor的初始化:
@Configuration public class XDRemoteDeliveryProcessor implements XDDeliveryCallback { protected final XDDeliveryCore deliveryCore; protected final SmtpGatewayMessageSource smtpMessageSource; protected final DSNCreator dsnCreator; public XDRemoteDeliveryProcessor(@Lazy XDDeliveryCore deliveryCore, SmtpGatewayMessageSource smtpMessageSource, DSNCreator dsnCreator) { this.deliveryCore = deliveryCore; this.smtpMessageSource = smtpMessageSource; this.dsnCreator = dsnCreator; } // 其余代码保持不变 }
方案2:指定Bean初始化顺序
在XDDeliveryConfig的xdDeliveryCore()方法上添加@DependsOn,强制Spring先初始化XDRemoteDeliveryProcessor:
@Configuration public class XDDeliveryConfig { // ... 其他字段 @Bean @ConditionalOnMissingBean @DependsOn("xDRemoteDeliveryProcessor") // 默认Bean名称为类名首字母小写 public XDDeliveryCore xdDeliveryCore() { // ... 原有代码不变 } }
如果XDRemoteDeliveryProcessor自定义了Bean名称,需要修改@DependsOn中的对应值。
方案3:重构代码消除循环依赖
最彻底的方式是调整业务逻辑,比如将XDRemoteDeliveryProcessor中调用deliveryCore.processAndDeliverXDMessage()的逻辑,改为通过Spring事件发布、中间层代理等方式,避免直接依赖XDDeliveryCore,从根源上消除循环依赖。
内容的提问来源于stack exchange,提问作者JackTheKnife

