RTFSC:SpringBoot 源码惊鸿一瞥

1、从主方法入手

SpringBoot应用常规的main方法如下:

@SpringBootApplication
public class SpringBootDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootDemoApplication.class, args);
    }
}

可以看到一个灵魂注解@SpringBootApplication , 按住ctrl点进去看:

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
    ....
    ....
}

注意到它是一个组合注解,三个大件@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan

2、注解:@SpringBootConfiguration

@SpringBootConfiguration入手,按住ctrl追进去,发现:

@Configuration
public @interface SpringBootConfiguration {
    .....
}

它其实就是一个@Configuration 注解,它标注在哪个类上,就表示当前类是一个配置类。

既然这样,那可以理解这个注解被包装又包装后标注在了SpringBootDemoApplication类上,也就是说Main方法所在的类也是一个配置类。

3、注解:@ComponentScan

这个注解非常简单,就是指定要扫描哪些包,按需调整使用。不多说~

4、注解:@EnableAutoConfiguration

按住ctrl点进去看:

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    ....
}

可以发现它又是个组合注解,翻译一下:

  • @AutoConfigurationPackage : 自动配置包;
  • @Import(AutoConfigurationImportSelector.class) : 导入这么个类,说人话就是把这个类放入IOC容器中。

4.1 注解:@AutoConfigurationPackage

ctrl进入源码一探究竟:

@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
       ....
}

它其实就是一个@import注解, 而import的含义就是给容器中导入一个组件。它给容器中导入了一个Registrar,这又是个啥?点进去,看它!!!

	static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

		@Override
		public void registerBeanDefinitions(AnnotationMetadata metadata, 			BeanDefinitionRegistry registry) {
			register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
		}

		@Override
		public Set<Object> determineImports(AnnotationMetadata metadata) {
			return Collections.singleton(new PackageImports(metadata));
		}

	}

发现它有两个方法,其实也就是Registrar在利用registerBeanDefinitions方法在向容器中国批量注入组件。到底在批量注册什么呢,断点打在register方法处,走起~

这个方法传入了两个参数,其中有一个是AnnotationMetadata metadata, 叫做注解的元信息

这个注解指的是@AutoConfigurationPackage , 可以看到注解的元信息包括如下内容。其中有一条表示该注解标在了哪个类上——其实就是启动程序所在的类。

回头看register方法,单独计算其中的入参new PackageImports(metadata).getPackageNames(),得到这个结果,得到了一个包名,很明显这个包就是启动程序所在的包

为什么是这个包名?因为@AutoConfigurationPackage 实际上就是标注在启动程序所在的类,那么根据注解元信息获取到的包名自然就是这个类所在的包。

总结:到这基本上就清楚了,@AutoConfigurationPackage 就是根据包名,将这个包下的所有组件批量注册进容器。(此处所说包名,即为启动程序所在的包名)这也就解释了默认的包扫描路径是启动程序所在的包

4.2 @Import(AutoConfigurationImportSelector.class)

@Import利用Selector机制给容器中批量导入组件,进入AutoConfigurationImportSelector,找到一个方法selectImports:

// 到底要导入哪些?这个数组中规定要导入哪些就导哪些
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
    
		AutoConfigurationEntry autoConfigurationEntry 
            // 所有的东西都是由这个方法得到的,重点看这个方法:
            = getAutoConfigurationEntry(annotationMetadata);
    
    	// 根据上边方法得到的结果,封装返回值
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}

断点打在 getAutoConfigurationEntry(annotationMetadata)(字面意思:获取所有自动配置的集合)处,Debug模式运行:

getCandidateConfigurations(annotationMetadata, attributes)执行后得到一个configurations集合,接着对这个数组做了多个操作,然后封装返回结果。

该方法执行后得到所有候选的配置类,一共是127个(SpringBoot版本是2.3.4.RELEASE)。也就是说SpringBoot应用启动之初默认导入127个配置类。

问题来了,导入规则依据什么? 进入``getCandidateConfigurations`方法一窥究竟。

利用Spring工厂加载器加载一些东西,点进去看。

拨开云雾见青天了,从META-INF/spring.factories位置来加载一个文件。

  • 默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件
  • 最核心的spring-boot-autoconfigure-2.3.4.RELEASE.jar包有META-INF/spring.factories

挖到祖坟了,不信你数,正好127个!!~~~ 撒花!!!

总结:SpringBoot一启动就要给容器加载所有的配置类就在spring-boot-autoconfigure-2.3.4.RELEASE.jar包有META-INF/spring.factories。这里有个问题,要加载这么多组件,而实际上容器里并不会有那么多的组件。其中哪些可以生效,哪些不生效,就是接下来要聊的:按需开启自动配置项。

4.3 按需开启自动配置项

虽然SpringBoot在启动的时候会默认加载127个场景的自动配置,但是究竟有哪些生效按照条件装配规则(@Conditional),最终会按需配置。

例如org.springframework.boot.autoconfigure.aop.AopAutoConfiguration

@Configuration(proxyBeanMethods = false)
// 按照规则:配置文件中有前缀spring.aop auto 的配置项
// havingValue = "true" 表示即便没配,默认为 true
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
// 按照上述规则,这个配置类是生效的!
public class AopAutoConfiguration {

	@Configuration(proxyBeanMethods = false)
    //当容器中有Advice这个类的时候,这个类才会生效。 下边同理·~~
	@ConditionalOnClass(Advice.class)
	static class AspectJAutoProxyingConfiguration {
		@Configuration(proxyBeanMethods = false)
		@EnableAspectJAutoProxy(proxyTargetClass = false)
		@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
				matchIfMissing = false)
		static class JdkDynamicAutoProxyConfiguration {
		}
        
		@Configuration(proxyBeanMethods = false)
		@EnableAspectJAutoProxy(proxyTargetClass = true)
		@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
				matchIfMissing = true)
		static class CglibAutoProxyConfiguration {
		}}
    
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnMissingClass("org.aspectj.weaver.Advice")
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
			matchIfMissing = true)
	static class ClassProxyingConfiguration {
	}
}
posted @ 2020-12-30 22:10  Simon-Lau  阅读(273)  评论(0编辑  收藏  举报