一次Spring NoSuchBeanDefinitionException排查过程

博客围绕产生NoSuchBeanDefinitionException异常展开,在对应类标注@Service注解且处于扫描路径下仍抛出异常。经排查,发现是ClassPathBeanDefinitionScanner扫描时未找到对应class文件,未注册BeanDefinition。推测是之前清除缓存操作有bug,解决方案是删除target目录重新编译。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

产生NoSuchBeanDefinitionException的一般原因,不清楚的同学可以先了解一波。
而此次场景有所不同,具体的排查定位过程如下。

问题描述

在该场景下,对应类【LoanQuestionListStrategy】标注了spring的@Service注解,并且处于【component-scan】扫描路径下。

@Service("loanQuestionListStrategy")
public class LoanQuestionListStrategy {...}

在部署的时候,意外发生了,抛出了NoSuchBeanDefinitionException name loanQuestionListStrategy异常。
具体的堆栈信息如下:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'loanQuestionListStrategy' available
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:687)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1207)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:284)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:522)
	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:496)
	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:627)
	at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:169)
	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessPropertyValues(CommonAnnotationBeanPostProcessor.java:318)
	... 71 more

追踪到at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:687)`
具体如下图所示:
在这里插入图片描述
发现在beanDefinitionMap中根据beanName获取BeanDefinition失败后会抛出该异常信息。
那么为什么beanDefinitionMap中没有【LoanQuestionListStrategy】对应的BeanDefinition呢?
既然beanDefinitionMap中没有,那么说明对应的BeanDefinition根本就没有put进去,自然而然要找到beanDefinitionMap-PUT的调用的地方。

在该场景下,唯一PUT的地方是在component-scan标签解析过程中进行的。
熟悉spring handlers机制的同学可能立马能够找到【component-scan】标签扫描并注册BeanDefinition的位置。
在这里插入图片描述
对应的类为:【ComponentScanBeanDefinitionParser】

在该类里会默认会扫描classpath*路径下标注【Component】【Repository】【Service】【Controller】注解的类,并将其转化为BeanDefinition存储在上述的beanDefinitionMap中。
具体步骤:如下所示:
第一步:构建ClassPathBeanDefinitionScanner扫描器
第二步:进行扫描,获取符合条件的BeanDefinitions
第三步:注册BeanDefinitions到上述的beanDefinitionMap
在这里插入图片描述
经过断点调试,发现第一步扫描根本就没有返回对应的BeanDefinition,于是继续深入【ClassPathBeanDefinitionScanner.doScan】方法,doScan方法通过调用【ClassPathBeanDefinitionScanner.findCandidateComponents】方法获取候选的BeanDefinition,在其方法调用中标注条件断点(metadataReader.getClassMetadata().getClassName().contains("LoanQuestionListStrategy"))
在这里插入图片描述
发现此断点没有生效。
what???,到此已经一脸甭比。
突然在【ClassPathBeanDefinitionScanner】类【findCandidateComponents(获取候选BeanDefinition)】方法中发现了如下红框所示的语句。
在这里插入图片描述
其中packageSearchPath表示ClassPathBeanDefinitionScanner.scan的范围,默认是ClassPath*下的所有以class结尾的文件。
在这里插入图片描述
在这里插入图片描述

那么会不会是没有对应的class文件呢?
证明发现:确实是没有。在这里插入图片描述
那么问题已经浮出水面,ClassPathBeanDefinitionScanner在classpath*路径下没有发现对应的资源文件,就不会注册BeanDefinition到beanDefinitionMap,那么在最终获取的时候就会报出NoSuchBeanDefinitionException的错误。

那么Class文件为什么丢了?

在这之前,我进行了一次清除缓存操作,怀疑该操作有bug。
在这里插入图片描述

解决方案:

删除掉target目录,重新编译即可。

<think>嗯,用户现在问的是如何用Spring XML来创建一个接口。之前的对话里,我已经详细介绍了Java接口的基础概念、实现步骤和现代用法,包括代码示例。用户现在可能是在实际项目中需要将接口与Spring框架结合使用,特别是通过XML配置的方式。 首先,我需要确认用户对Spring框架的了解程度。可能用户已经知道Spring的基本概念,但需要具体的配置方法。Spring XML配置是传统的方式,虽然现在更常用注解,但仍有老项目在使用XML,所以用户的需求是合理的。 接下来,我需要明确用户所说的“用上面那个springXML建一个接口”具体指什么。因为接口本身在Java中是定义的,不需要Spring来创建,但Spring可以通过XML配置来管理接口的实现类,以及依赖注入。因此,用户可能是指如何将接口和其实现类配置到Spring的XML文件中,以便通过容器来获取实例。 回顾之前的示例,用户给出了一个PaymentGateway接口和Alipay实现类。现在需要展示如何在Spring XML中配置这些,并可能演示依赖注入或服务引用。 需要注意Spring XML配置的基本结构:定义Bean,指定实现类,可能涉及属性注入或构造函数注入。由于接口不能被实例化,所以需要配置具体的实现类,但可以通过接口类型来引用。 可能需要提到<bean>标签的使用,以及如何通过ref或value注入依赖。另外,如果用户想使用接口进行依赖注入,需要确保在配置中正确指定实现类,这样Spring才能找到对应的Bean。 还需要考虑是否涉及其他Spring特性,比如AOP、事务管理等,但用户的问题可能仅限于基本配置,所以暂时不需要深入这些方面。 此外,用户可能有疑问,比如如何解决多个实现类的情况,这时候可能需要使用qualifier或指定具体的Bean ID。但当前示例中只有一个Alipay实现,所以可能不需要涉及这部分,但可以简要提及作为扩展。 最后,要确保提供的XML配置示例正确无误,包括必要的命名空间和标签闭合,避免用户复制时出错。同时,测试代码部分展示如何从ApplicationContext获取Bean,并调用方法,帮助用户验证配置是否正确。 可能还需要提醒用户注意配置文件的路径和类路径是否正确,以及确保Spring的相关依赖已经添加到项目中,否则即使配置正确也无法运行。</think>以下是使用Spring XML配置方式创建和使用接口的完整指南(基于之前的支付接口案例): --- ### 一、Spring XML配置基础步骤 #### 1. 创建接口与实现类(保持原代码不变) ```java // 接口 public interface PaymentGateway { boolean processPayment(double amount); } // 实现类 public class Alipay implements PaymentGateway { @Override public boolean processPayment(double amount) { System.out.println("支付宝处理支付:" + amount); return true; } } ``` #### 2. 创建Spring配置文件 `applicationContext.xml` ```xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 配置接口实现类 --> <bean id="alipayService" class="com.example.Alipay"/> <!-- 配置使用接口的服务 --> <bean id="paymentService" class="com.example.PaymentService"> <property name="paymentGateway" ref="alipayService"/> </bean> </beans> ``` --- ### 二、关键配置解析 #### 1. Bean定义规范 ```xml <bean id="唯一标识符" class="全限定类名"/> ``` - ✅ `id`:Spring容器中的唯一标识 - ✅ `class`:实现类的完整路径(非接口) #### 2. 依赖注入示例 ```java // 服务类 public class PaymentService { private PaymentGateway paymentGateway; // Setter注入 public void setPaymentGateway(PaymentGateway paymentGateway) { this.paymentGateway = paymentGateway; } public void executePayment(double amount) { paymentGateway.processPayment(amount); } } ``` --- ### 三、现代XML高级配置 #### 1. 多实现类场景 ```xml <!-- 多个实现 --> <bean id="alipay" class="com.example.Alipay"/> <bean id="wechatPay" class="com.example.WechatPay"/> <!-- 按名称自动装配 --> <bean id="paymentService" class="com.example.PaymentService" autowire="byName"/> ``` #### 2. 初始化/销毁方法 ```xml <bean id="alipay" class="com.example.Alipay" init-method="init" destroy-method="cleanup"/> ``` --- ### 四、实际使用示例 ```java public class MainApp { public static void main(String[] args) { // 加载Spring配置 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); // 获取服务实例 PaymentService service = context.getBean("paymentService", PaymentService.class); // 执行支付 service.executePayment(1000.0); } } ``` --- ### 五、XML配置 vs 注解配置 | 特性 | XML配置 | 注解配置 | |--------------------|--------------------------|-------------------| | 集中管理 | ✅ 所有配置在单一文件 | ❌ 分散在各个类中 | | 修改成本 | 需重新编译 | 无需编译直接修改 | | 依赖关系可见性 | 全局视图清晰 | 需要查看多个类 | | 现代Spring推荐度 | 传统方式(仍广泛使用) | ✅ 推荐方式 | --- ### 六、常见错误排查 1. **NoSuchBeanDefinitionException** ```xml <!-- 错误:class路径错误 --> <bean id="alipay" class="com.wrongpackage.Alipay"/> ✖ <!-- 正确 --> <bean id="alipay" class="com.example.Alipay"/> ✔ ``` 2. **依赖注入失败** ```java // 错误:缺少setter方法 public class PaymentService { private PaymentGateway paymentGateway; // 缺少setPaymentGateway()方法 ✖ } ``` --- ### 七、最佳实践建议 1. 项目结构规范: ``` src/ ├── main/ │ ├── java/ │ │ └── com/example/ │ │ ├── PaymentGateway.java │ │ ├── Alipay.java │ │ └── PaymentService.java │ └── resources/ │ └── applicationContext.xml ``` 2. 混合使用策略: ```xml <!-- 启用注解扫描 --> <context:component-scan base-package="com.example"/> <!-- 保留XML配置 --> <bean id="legacyService" class="com.example.LegacyServiceImpl"/> ``` 通过XML配置接口实现,可以实现解耦和灵活替换实现类(例如切换为`WechatPay`只需修改XML配置)。建议在新项目中优先使用`@Component`+`@Autowired`注解方式,但在维护旧系统时仍需熟练掌握XML配置。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值