我们也可以利用@Conditional来自定义条件注解
Starter,就是一个Maven依赖,当我们在项目的pom.xml文件中添加某个Starter依赖时,其实就是简单的添加了很多其他的依赖,比如:
如果硬要把Starter机制和自动配置联系起来,那就是通过@ConditionalOnClass这个条件注解,因为这个条件注解的作用就是用来判断当前应用的依赖中是否存在某个类或某些类,比如:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedTomcat {
@Bean
TomcatServletWebServerFactory tomcatServletWebServerFactory(
ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
ObjectProvider<TomcatContextCustomizer> contextCustomizers,
ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
// orderedStream()调用时会去Spring容器中找到TomcatConnectorCustomizer类型的Bean,默认是没有的,程序员可以自己定义
factory.getTomcatConnectorCustomizers()
.addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatContextCustomizers()
.addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatProtocolHandlerCustomizers()
.addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
return factory;
}
}
上面代码中就用到了@ConditionalOnClass,用来判断项目中是否存在Servlet.class、Tomcat.class、UpgradeProtocol.class这三个类,如果存在就满足当前条件,如果项目中引入了spring-boot-starter-tomcat,那就有这三个类,如果没有spring-boot-starter-tomcat那就可能没有这三个类(除非你自己单独引入了Tomcat相关的依赖)。
如果我们在项目中要用Tomcat,那就依赖spring-boot-starter-web就够了,因为它默认依赖了spring-boot-starter-tomcat,从而依赖了Tomcat,从而Tomcat相关的Bean能生效。
而如果不想用Tomcat,那就得这么写:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-tomcatartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-undertowartifactId>
dependency>
得把spring-boot-starter-tomcat给排除掉,再添加上spring-boot-starter-undertow的依赖,这样Tomcat的Bean就不会生效,Undertow的Bean就能生效,从而项目中用的就是Undertow
问题:先解析自动配置类中定义的Bean,还是用户定义的Bean
不管是用户定义的配置类还是自动配置类,都是配置类(简单理解就是加了@Configuration注解)
SpringBoot启动时,最核心的也就是创建一个Spring容器,而创建Spring容器的过程中会注解做几件事情:
Spring会在最后才来解析AutoConfigurationImportSelector这个配置类,而这个类的作用就是用来解析SpringBoot的自动配置类,那既然无法扫描到SpringBoot中的自动配置类,那怎么知道SpringBoot中有哪些自动配置类呢,这就需要spring.factories文件,默认情况下,SpringBoot会提供一个spring.factories文件,并把所有自动配置类的名字记录在这个文件中,到时候启动过程中解析这个文件就知道有哪些自动配置类了,并且这件事也是发生在解析完用户的配置类之后的。
可以发现这个注解上有另外三个注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
AutoConfigurationImportSelector实现了DeferredImportSelector这个接口,Spring容器在启动时,会在解析完其他所有程序员定义的配置类之后,来调用AutoConfigurationImportSelector中的selectImports方法,然后把该方法返回的类名对应的类作为配置类进行解析。
利用SpringFactoriesLoader找到所有的META-INF/spring.factories文件中key为EnableAutoConfiguration.class的value值,也就是众多自动配置类的类名
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 获取@EnableAutoConfiguration的属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 获取spring.factories中所有的AutoConfiguration
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 去重(也就是按类名去重)
configurations = removeDuplicates(configurations);
// 获取需要排除的AutoConfiguration,可以通过@EnableAutoConfiguration注解的exclude属性,或者spring.autoconfigure.exclude来配置
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 排除
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// 获取spring.factories中的AutoConfigurationImportFilter对AutoConfiguration进行过滤
// 默认会拿到OnBeanCondition、OnClassCondition、OnWebApplicationCondition
// 这三个会去判断上面的AutoConfiguration是否符合它们自身所要求的条件,不符合的会过滤掉,表示不会进行解析了
// 会利用spring-autoconfigure-metadata.properties中的配置来进行过滤
// spring-autoconfigure-metadata.properties文件中的内容是利用Java中的AbstractProcessor技术在编译时生成出来的
configurations = getConfigurationClassFilter().filter(configurations);
// configurations表示合格的,exclusions表示被排除的,把它们记录在ConditionEvaluationReportAutoConfigurationImportListener中
fireAutoConfigurationImportEvents(configurations, exclusions);
// 最后返回的AutoConfiguration都是符合条件的
return new AutoConfigurationEntry(configurations, exclusions);
}
Spring Boot 作者通过编译得到class文件
Spring 引起通过ASM技术跳过类加载机制获取注解上类的字符串信息,在通过反射catch错误
return new SpringApplication(primarySources).run(args);
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 1. 推测web应用类型(NONE、REACTIVE、SERVLET)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 2. 从spring.factories中获取BootstrapRegistryInitializer对象
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
// 3. 从spring.factories中获取ApplicationContextInitializer对象
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 4. 从spring.factories中获取ApplicationListener对象
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 5. 推测出Main类(main()方法所在的类)
this.mainApplicationClass = deduceMainApplicationClass();
}
public ConfigurableApplicationContext run(String... args) {
long startTime = System.nanoTime();
// 1、创建引导启动器,类似一个ApplicationContext,可以往里面添加一些对象
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
// 2、从spring.factories中获取SpringApplicationRunListener对象
// 默认会拿到一个EventPublishingRunListener,它会启动过程的各个阶段发布对应的ApplicationEvent事件
SpringApplicationRunListeners listeners = getRunListeners(args);
// 3、发布ApplicationStartingEvent
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 4、将run()的参数封装为DefaultApplicationArguments对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 5、准备Environment
// 包括操作系统,JVM、ServletContext、properties、yaml等等配置
// 会发布一个ApplicationEnvironmentPreparedEvent
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 默认spring.beaninfo.ignore=true,表示不需要jdk缓存beanInfo信息,Spring自己会缓存
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
// 6、根据应用类型创建Spring容器
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
// 7、利用ApplicationContextInitializer初始化Spring容器
// 8、发布ApplicationContextInitializedEvent
// 9、关闭DefaultBootstrapContext
// 10、注册primarySources类,就是run方法存入进来的配置类
// 11、发布ApplicationPreparedEvent事件
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 12、刷新Spring容器,会解析配置类、扫描、启动WebServer
refreshContext(context);
// 空方法
afterRefresh(context, applicationArguments);
// 启动时间
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
// 13、发布ApplicationStartedEvent事件,表示Spring容器已经启动
listeners.started(context, timeTakenToStartup);
// 14、从Spring容器中获取ApplicationRunner和CommandLineRunner,并执行其run()
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
// 15、发布ApplicationFailedEvent事件
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
// 16、发布ApplicationReadyEvent事件,表示Spring容器已经准备好了
listeners.ready(context, timeTakenToReady);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
https://www.processon.com/view/link/62d399e71e08530a89222b23