同事自己在自己的电脑上写了一些小东西,遇到了请求时API层注入的Service为空问题。整体代码接口伪代码如下:
- public Interface ParentService{
- public String hello(String msg);
- }
- public abstract class ChildrenOneService implements ParentService{
-
- @Override
- public String hello(String msg){
- System.out.println("ChildrenOne say"+msg);
- }
-
- }
- public abstract class ChildrenTwoService implements ParentService{
-
- @Override
- public String hello(String msg){
- System.out.println("ChildrenTwo say"+msg);
- }
-
- }
- @RestController
- public class TestApi{
- @Resource(type = ChildrenOneService.class)
- private Parent childrenOneService;
-
- @Resource(type = ChildrenTwoService.class)
- private Parent childrenTwoService;
-
- @PostMapping("/test1")
- public String sayOne(String msg){
- childrenOneService.hello(msg);
- }
-
- @PostMapping("/test2")
- public String sayTwo(String msg){
- childrenTwoService.hello(msg);
- }
-
- }
在执行过程中报空指针异常,打断点发现childrenOneService和childrenTwoService全部为空。直接在AbstractApplicationContext里的refresh方法里最后一行finishRefresh打断点查看beanFactory里的beanDefinitionMap里的bean是否存在。经过排查发现Map里没有这两个类型的Bean。
第一步:怀疑是Application.java的ComponentScan没有扫描到。
查看Application.java启动类,发现Application类上没有配置ComponentScan,那么SpringBoot默认扫描Application文件所在同级目录下的所有类。发现ChildrenOneService和ChildrenTwoService在Application文件所在的同级目录下。
第二步:怀疑是ChildrenOneService和ChildrenTwoService不符合bean的规范。检查这两个类发现这两个类的类定义里使用了abstract关键字来修饰类名。
- protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
- AnnotationMetadata metadata = beanDefinition.getMetadata();
- return (metadata.isIndependent() && (metadata.isConcrete() ||
- (metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
- }
在springboot源码中发现加载时会判断是否时符合要求的组件,其中发现组件是抽象的且被Lookup注解才会被加载。所以在将ChildrenOneService和ChildrenTwoService类由抽象改为普通类后,注入成功了。
改后(以ChildrenOneService为例):
- //去掉了abstract
- public class ChildrenOneService implements ParentService{
-
- @Override
- public String hello(String msg){
- System.out.println("ChildrenOne say"+msg);
- }
-
- }