因为想要学习Spring cloud,所以需要学习Spring boot。对于Spring boot主要有以下两点理解:
1.起步依赖
就好比,你要老妈子给你介绍,你只要关注介绍的这个人就好,至于老妈子怎么去托关系找你二大姑啊,三大姨来张罗啊,你都可以不用管了。
对于你需要的应用,它架包的传递依赖以及兼容性,spring boot都帮你做了,你无需再去各种架包引用,还得看是否兼容,大大提升了开发效率。
2.自动配置
自动配置,主要看classpath有没有要初始的bean,会自动进行配置,也可以覆盖自动配置,这里主要使用了spring的条件化配置。
下图是SpringApplication启动原理:
图1
自动配置
@SpringBootApplication主要涉及到以下三个注解:
@Configuration
@ComponentScan
@EnableAutoConfiguration(最重要)
Auto configure的加载:
@EnableAutoConfiguration --> @Import(导入自动配置) -->@EnableAutoConfigurationImportSelector
(由SpringFactoriesLoader.loadFactoryNames加载EnableAutoConfiguration对应的自动配置项)
@Configuration标记的类加载原理:
configuration类的加载主要是ConfigurationClassPostProcessor类的processConfigBeanDefinitions方法
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList();
String[] candidateNames = registry.getBeanDefinitionNames();
String[] var4 = candidateNames;
int var5 = candidateNames.length;
for(int var6 = 0; var6 < var5; ++var6) {
String beanName = var4[var6];
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if(!ConfigurationClassUtils.isFullConfigurationClass(beanDef) && !ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
if(ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
} else if(this.logger.isDebugEnabled()) {
this.logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
if(!configCandidates.isEmpty()) {
Collections.sort(configCandidates, new Comparator<BeanDefinitionHolder>() {
public int compare(BeanDefinitionHolder bd1, BeanDefinitionHolder bd2) {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return i1 < i2?-1:(i1 > i2?1:0);
}
});
SingletonBeanRegistry sbr = null;
if(registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry)registry;
if(!this.localBeanNameGeneratorSet && sbr.containsSingleton("org.springframework.context.annotation.internalConfigurationBeanNameGenerator")) {
BeanNameGenerator generator = (BeanNameGenerator)sbr.getSingleton("org.springframework.context.annotation.internalConfigurationBeanNameGenerator");
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
ConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet(configCandidates); // 一般启动时,这里就包含了你的启动类,如DemoApplication
HashSet alreadyParsed = new HashSet(configCandidates.size());
do {
// 这里进行转换,对标记了Configuration的类进行搜集(一般是自动配置的类以及它的依赖类),
// 像DemoApplication比较特殊,引入了@ComponentScan,所以会将父包下的Configuration类型的类也会进行搜集,所以如果显示设置配置,可覆盖自动设置(条件化加载)
// 这里的转换比较复杂,使用了很多的递归以及条件依赖(加载A时,先要加载B),暂不做详细研究,可重新作为另一方面来探讨,有兴趣的同学可以一起交流,具体流程可参考下图
parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
if(this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry());
}
// 这里主要是对每个标记为Configuration的类加载该类下的Bean配置(@Bean方法,ImportedResources(BeanDefinitionReader)引入的,Registrars注册器引入的)
// 如果自身是需要被引用的,首先将自身注册为bean,再去加载该类的bean配置
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
if(registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet();
Iterator var12 = alreadyParsed.iterator();
while(var12.hasNext()) {
ConfigurationClass configurationClass = (ConfigurationClass)var12.next();
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
String[] var23 = newCandidateNames;
int var24 = newCandidateNames.length;
// 已注册的bean,判断是没有进行转换的,则进行转换(candidates判空循环)
for(int var14 = 0; var14 < var24; ++var14) {
String candidateName = var23[var14];
if(!oldCandidateNames.contains(candidateName)) {
BeanDefinition bd = registry.getBeanDefinition(candidateName);
if(ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
} while(!candidates.isEmpty());
if(sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
if(this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
((CachingMetadataReaderFactory)this.metadataReaderFactory).clearCache();
}
}
}
自动配置的类加载过程:
图2
在processImports方法中,会加载自动配置所对应的类(spring.factories下的配置)。
以上纯属个人理解,如有错误,请见谅,如可以请联系我,让我把错误修正,感谢。