记录一次SpringBoot自动装配遇到的问题:
情况是这样的,我本地用的gradle,配置什么的无任何问题,但是其中一个子项目的源码中的一个控制层的controller在浏览器访问怎么都是404,头疼!!!
最后研究spring的bean装配,发现Spring会装入启动类路径下的所有被 组件类注解(@Component,@Configuration,@Controller,@Service等等)标注的类;但是问题来了,我的找不到路径的是一个用到的jar包呀,这时我只能排查自动装配流程,为什么会出现浏览器URL映射不到RequestMapping,以下是我的分析流程:
1.查看@SpringBootConfiguration;
2.查看里面的@EnableAutoConfiguration注解,自动装配功能主要是此注解实现的;
3.再去看此注解@Import({AutoConfigurationImportSelector.class}),AutoConfigurationImportSelector类是实现自动装配的关键;
4.然后看下面的process方法:
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
() -> String.format("Only %s implementations are supported, got %s",
AutoConfigurationImportSelector.class.getSimpleName(),
deferredImportSelector.getClass().getName()));
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
4.再看核心的getAutoConfigurationEntry方法:
/**
* Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata}
* of the importing {@link Configuration @Configuration} class.
* @param annotationMetadata the annotation metadata of the configuration class
* @return the auto-configurations that should be imported
*/
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
5.其中此方法就获取到了jar包中的所有@Configuration的类,包括此类若果有@ComponentScan还会扫票配置的包下的所有组件类;核心在下面所示代码:
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
6.进入getCandidateConfigurations方法的实现方法AutoConfigurationImportSelector.getCandidateConfigurations中查看
/**
* Return the auto-configuration class names that should be considered. By default
* this method will load candidates using {@link SpringFactoriesLoader} with
* {@link #getSpringFactoriesLoaderFactoryClass()}.
* @param metadata the source metadata
* @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
* attributes}
* @return a list of candidate configurations
*/
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
此方法清晰的说明了回去所有jar包下找META-INF/spring.factories配置文件,此文件标注了要被自动装配的类。
7.样例:
比如上图所示,Spring会吧此jar包下的此类 和 此jar包com.jiuqi.nr.sso路径下的所有组件类自动装配到容器中。
8.我的问题:
原理搞懂后,很清晰的定位 到了自己的问题:
我浏览器映射不上RequestMapping的controller所在的jar包没有配置spring.factories配置文件,可能是在写此jar包的源码的时候,忘记提交这个配置文件了,所以导致此问题,补充上此配置文件并在配置文件中配置了一个@configuration类的路径,就是上图中的类,它会将此类和com.jiuqi.nr.sso下的所有组件类注册到容器中,具体spring.factories配置文件如下:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.jiuqi.nr.sso.config.SsoConfig
将jar包源码拉下,同时添加上此配置文件后重新打jar包后,并更新jar包,发现问题成功解决!
特此记录。