产生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目录,重新编译即可。