1. 从SpringBootApplication开始
1import org.springframework.boot.SpringApplication; 2import org.springframework.boot.autoconfigure.SpringBootApplication; 3@SpringBootApplication 4public class Start { 5 6 public static void main(String[] args) { 7 SpringApplication.run(Start.class, args); 8 } 9 10}
通过main方法启动,进入,跟进去:
1public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { 2 return new SpringApplication(primarySources).run(args); 3}
发现是通过SpringApplication的run来启动的,创建容器的方法是createApplicationContext:
1protected ConfigurableApplicationContext createApplicationContext() { 2 return this.applicationContextFactory.create(this.webApplicationType); 3}
最骚操作的是ApplicationContextFactory这个类,自己拿了一个自己的实现类,还是lambda表达式,所以很有迷惑性,当时我一直以为我错过了什么Java的重要特性了。
1ApplicationContextFactory DEFAULT = (webApplicationType) -> { 2 try { 3 switch (webApplicationType) { 4 case SERVLET: 5 return new AnnotationConfigServletWebServerApplicationContext(); 6 case REACTIVE: 7 return new AnnotationConfigReactiveWebServerApplicationContext(); 8 default: 9 return new AnnotationConfigApplicationContext(); 10 } 11 } 12 catch (Exception ex) { 13 throw new IllegalStateException("Unable create a default ApplicationContext instance, " 14 + "you may need a custom ApplicationContextFactory", ex); 15 } 16};
Spring容器就创建好了,后面基本就是进入Spring模式的核心refresh了,然后就开始扫描解析BeanDefinition等等操作。
2. @SpringBootApplication注解
1@SpringBootConfiguration 2@EnableAutoConfiguration 3@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), 4 @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) 5public @interface SpringBootApplication { 6}
@SpringBootConfiguration:其实就是@Configuration,会通过ConfigurationClassPostProcessor处理,会在Spring处理BeanFactoryPostProcessor阶段被处理 @ComponentScan:这个我们比较熟悉了扫描Bean的
SpringBoot中的新重点是@EnableAutoConfiguration:
1@AutoConfigurationPackage 2@Import(AutoConfigurationImportSelector.class) 3public @interface EnableAutoConfiguration { 4}
这个注解Import了AutoConfigurationImportSelector,AutoConfigurationImportSelector继承了ImportSelector,当被Import导入的时候就会执行它的selectImports。 关于Import注解的逻辑可以参考后面的Import相关的内容。
最终在AutoConfigurationImportSelector的getCandidateConfigurations方法中通过:
SpringFactoriesLoader#loadFactoryNames方法导入了配置文件META-INF/spring.factories配置的类。
spring.factories配置文件中配置了一些XXXAutoConfiguration类,这些类又会导入一些类,例如spring-boot依赖的spring-boot-autoconfigure的META-INF/spring.factories中配置了一个ServletWebServerFactoryAutoConfiguration类。
1@Configuration(proxyBeanMethods = false) 2@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) 3@ConditionalOnClass(ServletRequest.class) 4@ConditionalOnWebApplication(type = Type.SERVLET) 5@EnableConfigurationProperties(ServerProperties.class) 6@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, 7 ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, 8 ServletWebServerFactoryConfiguration.EmbeddedJetty.class, 9 ServletWebServerFactoryConfiguration.EmbeddedUndertow.class }) 10public class ServletWebServerFactoryAutoConfiguration { 11 12 @Bean 13 public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties, 14 ObjectProvider<WebListenerRegistrar> webListenerRegistrars) { 15 return new ServletWebServerFactoryCustomizer(serverProperties, 16 webListenerRegistrars.orderedStream().collect(Collectors.toList())); 17 } 18}
它又会通过@Import、@Bean注解导入一些它需要的类。
有2个地方需要注意:
- 有@Conditional注解表示满足条件才导入,例如@ConditionalOnClass(ServletRequest.class),表示有ServletRequest这个类才导入这个类
- 我们在配置文件中添加server.port=8080为什么生效,就是@EnableConfigurationProperties(ServerProperties.class)配置的
具体的内容,可以看看后面的相关内容。
spring.factories不一定都是AutoConfiguration类,例如:
在spring-boot中: 
在spring-boot-autoconfigure中: 
3. @Conditional
关于了解一下下面的2个东西。
首先,它会在下面3个地方被处理:
- ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForBeanMethod:处理@Bean
- ConfigurationClassParser.processConfigurationClass(ConfigurationClass):处理@Configuration
- ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClass, SourceClass):处理@ComponentScan
常见的@Conditional注解,及其作用:
Conditional注解
作用
@ConditionalOnJndi
在JNDI存在的条件下查找指定的位置
@ConditionalOnBean
当容器里有指定Bean的条件下
@ConditionalOnJava
基于JVM版本作为判断条件
@ConditionalOnClass
当类路径下有指定的类的条件下
@ConditionalOnProperty
指定的属性是否有指定的值
@ConditionalOnResource
类路径是否有指定的值
@ConditionalOnExpression
基于SpEL表达式为true的时候作为判断条件才去实例化
@ConditionalOnMissingBean
当容器里没有指定Bean的情况下
@ConditionalOnMissingClass
当容器里没有指定类的情况下
@ConditionalOnWebApplication
当前项目时Web项目的条件下
@ConditionalOnOnSingleCandidate
当指定Bean在容器中只有一个,或者有多个但是指定首选的Bean
@ConditionalOnNotWebApplication
当前项目不是Web项目的条件下
关于@Conditional,可以看一下Spring Conditional原理与实例这篇文章。
4. @Import
@Import注解的参数有3种:
-
普通类直接注入
-
实现ImportSelector接口的类
-
实现ImportBeanDefinitionRegistrar接口的类
public class ImportSelectorTest implements ImportSelector {
1@Override 2public String[] selectImports(AnnotationMetadata importingClassMetadata) { 3 return new String[]{"vip.mycollege.User","vip.mycollege.Teacher"}; 4}}
public class ImportBeanDefinitionRegistrarTest implements ImportBeanDefinitionRegistrar {
1@Override 2public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { 3 RootBeanDefinition beanDefinition = new RootBeanDefinition(Teacher.class); 4 registry.registerBeanDefinition("teacher", beanDefinition); 5}}
处理Import的类是ConfigurationClassPostProcessor,这是一个BeanFactoryPostProcessor。在Spring核心流程梳理中我们介绍了这个是在BeanDefinition都解析好之后,在AbstractApplicationContext#refresh方法中通过invokeBeanFactoryPostProcessors来处理。
其中:@Import注解的处理逻辑在:ConfigurationClassParser的processImports方法中。
1for (SourceClass candidate : importCandidates) { 2 //处理ImportSelector 3 if (candidate.isAssignable(ImportSelector.class)) { 4 if (selector instanceof DeferredImportSelector) { 5 this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); 6 }else { 7 String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); 8 Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter); 9 processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false); 10 } 11 } 12 //处理ImportBeanDefinitionRegistrar 13 else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { 14 Class<?> candidateClass = candidate.loadClass(); 15 ImportBeanDefinitionRegistrar registrar = 16 ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class, 17 this.environment, this.resourceLoader, this.registry); 18 configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); 19 } 20 //处理其他类 21 else { 22 this.importStack.registerImport( 23 currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); 24 processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter); 25 } 26}
5. @EnableConfigurationProperties
1@Import(EnableConfigurationPropertiesRegistrar.class) 2public @interface EnableConfigurationProperties { 3}
EnableConfigurationPropertiesRegistrar又是一个ImportBeanDefinitionRegistrar。
主要注册了2个类:
- ConfigurationPropertiesBindingPostProcessor
- BoundConfigurationProperties
主要是ConfigurationPropertiesBindingPostProcessor是一个BeanPostProcessor,实现了postProcessBeforeInitialization,会在一个bean的属性值设置完之后,但是还没有执行init-method、 afterPropertiesSet等初始化之前被调用。
关于Spring Bean的生命周期可以看看:Spring Bean生命周期
具体处理逻辑是:
-
通过ConfigurationPropertiesBean的create方法找到@ConfigurationProperties注解并处理验证相关逻辑
-
通过ConfigurationPropertiesBindingPostProcessor自己的bind方法执行绑定操作
-
bind的逻辑是在ConfigurationPropertiesBinder的bind方法,它会去找到并解析@ConfigurationProperties注解
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true) public class ServerProperties {
1/** 2 * Server HTTP port. 3 */ 4private Integer port;}
所以我们在配置文件中配置的server.port=8080会生效。
6. 自定义starter
了解了上面的原理之后,自定义starter就没啥难的了。
一般官方的artifactId是spring-boot-starter-web,这种spring-boot-starter-xxx。
我们自定义的artifactId会使用:druid-spring-boot-starter,这种xxx-spring-boot-starter
只需要定义一个XXXAutoConfigure:
1@Configuration 2@EnableConfigurationProperties({NBProperties.class}) 3@Import({NBConfiguration.class}) 4public class NBDataSourceAutoConfigure { 5 6 @Bean(initMethod = "init") 7 @ConditionalOnMissingBean 8 public DataSource dataSource() { 9 return new NBDataSourceWrapper(); 10 } 11}
通过@Import注解添加一下默认配置需要的类,通过@Bean注入一下默认需要的类实例。
然后在resources目录下创建一个META-INF目录,创建一个spring.factories文件,把自己的XXXAutoConfigure配置进去就可以了。
1org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2vip.mycollege.autoconfig.DruidDataSourceAutoConfigure
如果你高兴,还可以加一个XXXProperties:
1@ConfigurationProperties("wo.nb") 2public class NBProperties { 3 private String realNB;