目录
1.AutoConfigurationPackages.Registrar.class 批量注册组件
2.AutoConfigurationImportSelector.class
3、DeferredImportSelector和ImportSelector
一、@SpringBootApplication
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration // 就是@Configuration。代表当前是一个配置类
@EnableAutoConfiguration // 开启自动配置
// 指定扫描哪些,Spring注解
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
二、@EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage //
@Import(AutoConfigurationImportSelector.class) // 给容器中导入一个组件
public @interface EnableAutoConfiguration {
1.AutoConfigurationPackages.Registrar.class 批量注册组件
这个操作,会将当前配置类的包路径,封装为一个Bean,可以供其他应用使用(比如mybatis)。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class) //给容器中导入一个组件
public @interface AutoConfigurationPackage {
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));
}
}
2.AutoConfigurationImportSelector.class
AutoConfigurationImportSelector实现了DeferredImportSelector方法,所以它注册的Bean都是延迟注册的(比用户自己注册的要晚)。
//org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#selectImports
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 读取自动配置类(spring.factories中导入的)
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
(1)、利用getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件
(2)、调用List configurations = getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要导入到容器中的配置类
(3)、利用工厂加载 Map<String, List> loadSpringFactories(@Nullable ClassLoader classLoader);得到所有的组件
(4)、从META-INF/spring.factories位置来加载一个文件。
默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件
文件里面写死了spring-boot一启动就要给容器中加载的所有配置类
spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面也有META-INF/spring.factories
虽然我们127个场景的所有自动配置启动的时候默认全部加载。xxxxAutoConfiguration
按照条件装配规则(@Conditional),最终会按需配置。
// org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry
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
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// 过滤,使用filter进行过滤,这也是个扩展点
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
3、DeferredImportSelector和ImportSelector
SpringBoot的自动配置为什么要使用DeferredImportSelector而不是ImportSelector?
DeferredImportSelector和ImportSelector的区别在于:
- 在解析ImportSelector时,所导入的配置类会被直接解析,而DeferredImportSelector导入的配置类会延迟进行解析(延迟在其他配置类都解析完之后)
- DeferredImportSelector支持分组,可以实现getImportGroup方法以及定义Group对象,就相当于指定了DeferredImportSelector所导入进来的配置类所属的组,比如SpringBoot就把所有自动配置类单独做了分组AutoConfigurationGroup
有些同学喜欢在自己项目中,使用@Bean时,有时候会仿照SpringBoot也加上@ConditionalOnBean等条件注解,但是这是一种错误的行为,因为自己项目中BeanDefinition加载顺序是无法确定的。
4、扩展:排除自动配置类
可以使用注解排除指定的类:
编辑
编辑
可以通过配置属性排除指定的类:
spring.autoconfigure.exclude=com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
可以在spring.factories设置一个key:org.springframework.boot.autoconfigure.AutoConfigurationImportFilter,value为AutoConfigurationImportFilter的实现类,可以使用BeanClassLoaderAware、BeanFactoryAware、EnvironmentAware、ResourceLoaderAware。
public class MyAutoConfigurationFilter implements AutoConfigurationImportFilter {
@Override
public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
boolean[] matches = new boolean[autoConfigurationClasses.length];
for (int i = 0; i < autoConfigurationClasses.length; i++) {
// 每一个进行判断,并判断是true还是false,进行 过滤
matches[i] = !StringUtils.equals(autoConfigurationClasses[i], WebSessionIdResolverAutoConfiguration.class.getName());
}
return matches;
}
}
编辑
5、spring.factories文件扩展
在META-INF/spring.factories文件中,定义需要自动配置的类:
key-value的形式,自动配置类的key为org.springframework.boot.autoconfigure.EnableAutoConfiguration。
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.autoconfigure.integration.IntegrationPropertiesEnvironmentPostProcessor
# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.r2dbc.R2dbcRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\
org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration,\
org.springframework.boot.autoconfigure.netty.NettyAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration,\
org.springframework.boot.autoconfigure.r2dbc.R2dbcTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketRequesterAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketServerAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketStrategiesAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.rsocket.RSocketSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.saml2.Saml2RelyingPartyAutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration,\
org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration,\
org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.ReactiveMultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.WebSessionIdResolverAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration
# Failure analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.autoconfigure.data.redis.RedisUrlSyntaxFailureAnalyzer,\
org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,\
org.springframework.boot.autoconfigure.flyway.FlywayMigrationScriptMissingFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer,\
org.springframework.boot.autoconfigure.jooq.NoDslContextBeanFailureAnalyzer,\
org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryBeanCreationFailureAnalyzer,\
org.springframework.boot.autoconfigure.r2dbc.MissingR2dbcPoolDependencyFailureAnalyzer,\
org.springframework.boot.autoconfigure.r2dbc.MultipleConnectionPoolConfigurationsFailureAnalzyer,\
org.springframework.boot.autoconfigure.r2dbc.NoConnectionFactoryBeanFailureAnalyzer,\
org.springframework.boot.autoconfigure.session.NonUniqueSessionRepositoryFailureAnalyzer
# Template availability providers
org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.web.servlet.JspTemplateAvailabilityProvider
# DataSource initializer detectors
org.springframework.boot.sql.init.dependency.DatabaseInitializerDetector=\
org.springframework.boot.autoconfigure.flyway.FlywayMigrationInitializerDatabaseInitializerDetector
# Depends on database initialization detectors
org.springframework.boot.sql.init.dependency.DependsOnDatabaseInitializationDetector=\
org.springframework.boot.autoconfigure.batch.JobRepositoryDependsOnDatabaseInitializationDetector,\
org.springframework.boot.autoconfigure.quartz.SchedulerDependsOnDatabaseInitializationDetector,\
org.springframework.boot.autoconfigure.session.JdbcIndexedSessionRepositoryDependsOnDatabaseInitializationDetector
三、按条件开启自动配置
1.得益于按条件装配@Conditional
编辑
可修饰在类、方法上。
1. ConditionalOnBean:是否存在某个某类或某个名字的Bean
2. ConditionalOnMissingBean:是否缺失某个某类或某个名字的Bean
3. ConditionalOnSingleCandidate:是否符合指定类型的Bean只有一个
4. ConditionalOnClass:是否存在某个类
5. ConditionalOnMissingClass:是否缺失某个类
6. ConditionalOnExpression:指定的表达式返回的是true还是false
7. ConditionalOnJava:判断Java版本
8. ConditionalOnWebApplication:当前应用是不是一个Web应用
9. ConditionalOnNotWebApplication:当前应用不是一个Web应用
10. ConditionalOnProperty:Environment中是否存在某个属性
// 指定在。。。之前配置在..之后配置
@AutoConfigureBefore
@AutoConfigureAfter
2、@ConditionalOnClass原理
当一个配置类或者Bean上面标注了@ConditionOnClass:
编辑
OnClassCondition继承了SpringBootCondition,而SpringBootCondition实现了Condition接口,就会执行matches方法,如果返回true就校验通过,返回false就校验不通过。(这个过程是在BeanDefinition定义时完成的)
// 这是Spring中的源码,不是SpringBoot中的
// conditions中保存了两个OnClassCondition对象,
//这段代码会依次调用OnClassCondition对象的matches方法进行条件匹配,
//一旦某一个条件不匹配就不会进行下一个条件的判断了,
//这里return的是true,但是这段代码所在的方法叫做shouldSkip,所以true表示忽略。
for (Condition condition : conditions) {
ConfigurationPhase requiredPhase = null;
if (condition instanceof ConfigurationCondition) {
requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
}
// 重点在这
if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
return true;
}
}
SpringBoot的条件注解,都是继承了SpringBootCondition的,在SpringBootCondition的matches方法中进行统一处理。
// org.springframework.boot.autoconfigure.condition.SpringBootCondition#matches(org.springframework.context.annotation.ConditionContext, org.springframework.core.type.AnnotatedTypeMetadata)
@Override
public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 针对每个条件注解进行条件判断
// 条件注解写在哪个类上,或哪个方法上
String classOrMethodName = getClassOrMethodName(metadata);
try {
// 条件的判断结果,在每个子类中进行实现
ConditionOutcome outcome = getMatchOutcome(context, metadata);
// 如果log的日志级别为trace,那就直接记录当前条件的判断结果
logOutcome(classOrMethodName, outcome);
// 将判断结果记录到ConditionEvaluationReport中
// ConditionEvaluationReporyLoggingListener会在收到ContextRefreshedEvent事件后把判断结果用于最终打印
// 也算是一个扩展点,可以自定义一个监听器,进行自己的特殊处理
recordEvaluation(context, classOrMethodName, outcome);
return outcome.isMatch();
}
catch (NoClassDefFoundError ex) {
throw new IllegalStateException("Could not evaluate condition on " + classOrMethodName + " due to "
+ ex.getMessage() + " not found. Make sure your own configuration does not rely on "
+ "that class. This can also happen if you are "
+ "@ComponentScanning a springframework package (e.g. if you "
+ "put a @ComponentScan in the default package by mistake)", ex);
}
catch (RuntimeException ex) {
throw new IllegalStateException("Error processing condition on " + getName(metadata), ex);
}
}
具体的条件匹配逻辑在getMatchOutcome方法中,而SpringBootCondition类中的getMatchOutcome方法是一个抽象方法,具体的实现逻辑就在子类OnClassCondition中:
// org.springframework.boot.autoconfigure.condition.OnClassCondition#getMatchOutcome
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
ClassLoader classLoader = context.getClassLoader();
ConditionMessage matchMessage = ConditionMessage.empty();
// 拿到ConditionalOnClass注解中的value值,也就是要判断是否存在的类名
List<String> onClasses = getCandidates(metadata, ConditionalOnClass.class);
if (onClasses != null) {
// 判断onClasses中不存在的类
List<String> missing = filter(onClasses, ClassNameFilter.MISSING, classLoader);
// 如果有缺失的类,那就表示不匹配
if (!missing.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class)
.didNotFind("required class", "required classes").items(Style.QUOTE, missing));
}
// 否则就表示匹配
matchMessage = matchMessage.andCondition(ConditionalOnClass.class)
.found("required class", "required classes")
.items(Style.QUOTE, filter(onClasses, ClassNameFilter.PRESENT, classLoader));
}
// 和上面类似,只不过是判断onMissingClasses是不是全部缺失,如果是则表示匹配
List<String> onMissingClasses = getCandidates(metadata, ConditionalOnMissingClass.class);
if (onMissingClasses != null) {
List<String> present = filter(onMissingClasses, ClassNameFilter.PRESENT, classLoader);
if (!present.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnMissingClass.class)
.found("unwanted class", "unwanted classes").items(Style.QUOTE, present));
}
matchMessage = matchMessage.andCondition(ConditionalOnMissingClass.class)
.didNotFind("unwanted class", "unwanted classes")
.items(Style.QUOTE, filter(onMissingClasses, ClassNameFilter.MISSING, classLoader));
}
return ConditionOutcome.match(matchMessage);
}
在getMatchOutcome方法中的逻辑为:
- 如果类或方法上有@ConditionalOnClass注解,则获取@ConditionalOnClass注解中的value属性,也就是要判断是否存在的类名
- 利用ClassNameFilter.MISSING来判断这些类是否缺失,把缺失的类的类名存入missing集合
- 如果missing不为空,则表示有类缺失,则表示不匹配,并利用ConditionMessage记录哪些类是缺失的,直接return,表示条件不匹配
- 否则,则表示条件匹配,继续执行代码
- 如果类或方法上有ConditionalOnMissingClass注解,则获取ConditionalOnMissingClass注解中的value属性,也就是要判断是否缺失的类名
- 利用ClassNameFilter.PRESENT来判断这些类是否存在,把存在的类的类名存入present集合
- 如果present不为空,则表示有类存在,则表示不匹配,并利用ConditionMessage记录哪些类是存在的,直接return,表示条件不匹配
- 否则,则表示条件匹配,继续执行代码
- return,表示条件匹配
因为ConditionalOnClass注解和ConditionalOnMissingClass注解的逻辑是比较类似的,所以在源码中都是在OnClassCondition这个类中实现的,假如一个类上即有@ConditionalOnClass,也有@ConditionalOnMissingClass:
@Configuration
@ConditionalOnClass(Tomcat.class)
@ConditionalOnMissingClass(value = "com.test")
public class MyConfiguration {
}
- 如果@ConditionalOnClass条件匹配、@ConditionalOnMissingClass条件也匹配,那么getMatchOutcome方法会执行两次
- 如果@ConditionalOnClass条件不匹配,那么getMatchOutcome方法会执行一次
- 如果@ConditionalOnClass条件匹配、@ConditionalOnMissingClass条件不匹配,那么getMatchOutcome方法也只会执行一次,因为在getMatchOutcome方法处理了这种情况
上面提到的ClassNameFilter.MISSING和ClassNameFilter.PRESENT也比较简单,代码如下:
protected enum ClassNameFilter {
PRESENT {
@Override
public boolean matches(String className, ClassLoader classLoader) {
return isPresent(className, classLoader);
}
},
MISSING {
@Override
public boolean matches(String className, ClassLoader classLoader) {
return !isPresent(className, classLoader);
}
};
abstract boolean matches(String className, ClassLoader classLoader);
static boolean isPresent(String className, ClassLoader classLoader) {
if (classLoader == null) {
classLoader = ClassUtils.getDefaultClassLoader();
}
try {
resolve(className, classLoader);
return true;
}
catch (Throwable ex) {
return false;
}
}
}
protected static Class<?> resolve(String className, ClassLoader classLoader) throws ClassNotFoundException {
if (classLoader != null) {
return Class.forName(className, false, classLoader);
}
return Class.forName(className);
}
主要就是用类加载器,来判断类是否存在。
3、@ConditionalOnBean的底层工作原理
@ConditionalOnBean和@ConditionalOnClass的底层实现应该是差不多的,一个是判断Bean存不存在,一个是判断类存不存在,事实上也确实差不多。
首先@ConditionalOnBean和@ConditionalOnMissingBean对应的都是OnBeanCondition类,OnBeanCondition类也是继承了SpringBootCondition,所以SpringBootCondition类中的getMatchOutcome方法才是匹配逻辑:
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
ConditionMessage matchMessage = ConditionMessage.empty();
MergedAnnotations annotations = metadata.getAnnotations();
// 如果存在ConditionalOnBean注解
if (annotations.isPresent(ConditionalOnBean.class)) {
Spec<ConditionalOnBean> spec = new Spec<>(context, metadata, annotations, ConditionalOnBean.class);
MatchResult matchResult = getMatchingBeans(context, spec);
// 如果某个Bean不存在
if (!matchResult.isAllMatched()) {
String reason = createOnBeanNoMatchReason(matchResult);
return ConditionOutcome.noMatch(spec.message().because(reason));
}
// 所有Bean都存在
matchMessage = spec.message(matchMessage).found("bean", "beans").items(Style.QUOTE,
matchResult.getNamesOfAllMatches());
}
// 如果存在ConditionalOnSingleCandidate注解
if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) {
Spec<ConditionalOnSingleCandidate> spec = new SingleCandidateSpec(context, metadata, annotations);
MatchResult matchResult = getMatchingBeans(context, spec);
// Bean不存在
if (!matchResult.isAllMatched()) {
return ConditionOutcome.noMatch(spec.message().didNotFind("any beans").atAll());
}
// Bean存在
Set<String> allBeans = matchResult.getNamesOfAllMatches();
// 如果只有一个
if (allBeans.size() == 1) {
matchMessage = spec.message(matchMessage).found("a single bean").items(Style.QUOTE, allBeans);
}
else {
// 如果有多个
List<String> primaryBeans = getPrimaryBeans(context.getBeanFactory(), allBeans,
spec.getStrategy() == SearchStrategy.ALL);
// 没有主Bean,那就不匹配
if (primaryBeans.isEmpty()) {
return ConditionOutcome.noMatch(
spec.message().didNotFind("a primary bean from beans").items(Style.QUOTE, allBeans));
}
// 有多个主Bean,那就不匹配
if (primaryBeans.size() > 1) {
return ConditionOutcome
.noMatch(spec.message().found("multiple primary beans").items(Style.QUOTE, primaryBeans));
}
// 只有一个主Bean
matchMessage = spec.message(matchMessage)
.found("a single primary bean '" + primaryBeans.get(0) + "' from beans")
.items(Style.QUOTE, allBeans);
}
}
// 存在ConditionalOnMissingBean注解
if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
Spec<ConditionalOnMissingBean> spec = new Spec<>(context, metadata, annotations,
ConditionalOnMissingBean.class);
MatchResult matchResult = getMatchingBeans(context, spec);
//有任意一个Bean存在,那就条件不匹配
if (matchResult.isAnyMatched()) {
String reason = createOnMissingBeanNoMatchReason(matchResult);
return ConditionOutcome.noMatch(spec.message().because(reason));
}
// 都不存在在,则匹配
matchMessage = spec.message(matchMessage).didNotFind("any beans").atAll();
}
return ConditionOutcome.match(matchMessage);
}
逻辑流程为:
- 当前在解析的类或方法上,是否有@ConditionalOnBean注解,如果有则生成对应的Spec对象,该对象中包含了用户指定的,要判断的是否存在的Bean的类型
- 调用getMatchingBeans方法进行条件判断,MatchResult为条件判断结果
- 只要判断出来某一个Bean不存在,则return,表示条件不匹配
- 只要所有Bean都存在,则继续执行下面代码
- 当前在解析的类或方法上,是否有@ConditionalOnSingleCandidate注解,如果有则生成对应的SingleCandidateSpec对象,该对象中包含了用户指定的,要判断的是否存在的Bean的类型(只能指定一个类型),并且该类型的Bean只能有一个
- 调用getMatchingBeans方法进行条件判断,MatchResult为条件判断结果
- 指定类型的Bean如果不存在,则return,表示条件不匹配
- 如果指定类型的Bean存在,但是存在多个,那就看是否存在主Bean(加了@primary注解的Bean),并且只能有一个主Bean,如果没有,则return,表示条件不匹配
- 如果只有一个主Bean,则表示条件匹配,继续执行下面代码
- 当前在解析的类或方法上,是否有@ConditionalOnMissingBean注解,如果有则生成对应的Spec对象,该对象中包含了用户指定的,要判断的是否缺失的Bean的类型
- 调用getMatchingBeans方法进行条件判断,MatchResult为条件判断结果
- 只要有任意一个Bean存在,则return,表示条件不匹配
- 都存在,则表示条件匹配
- 结束
getMatchingBeans方法中会利用BeanFactory去获取指定类型的Bean,如果没有指定类型的Bean,则会将该类型记录在MatchResult对象的unmatchedTypes集合中,如果有该类型的Bean,则会把该Bean的beanName记录在MatchResult对象的matchedNames集合中,所以MatchResult对象中记录了,哪些类没有对应的Bean,哪些类有对应的Bean。
@ConditionalOnClass和@ConditionalOnBean,这两个条件注解的工作原理就分析到这,总结以下流程就是:
- Spring在解析某个配置类,或某个Bean定义时
- 如果发现它们上面用到了条件注解,就会取出所有的条件的条件注解,并生成对应的条件对象,比如OnBeanCondition对象、OnClassCondition对象
- 从而依次调用条件对象的matches方法,进行条件匹配,看是否符合条件
- 而条件匹配逻辑中,会拿到@ConditionalOnClass和@ConditionalOnBean等条件注解的信息,比如要判断哪些类存在、哪些Bean存在
- 然后利用ClassLaoder、BeanFactory来进行判断
- 最后只有所有条件注解的条件都匹配,那么当前配置类或Bean定义才算符合条件
4、自定义条件注解
一般情况下是不需要自定义条件注解的,SpringBoot提供的内置条件注解已经很齐全了。
@Conditional注解的定义,需要设置value为一个Condition:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
/**
* All {@link Condition} classes that must {@linkplain Condition#matches match}
* in order for the component to be registered.
*/
Class<? extends Condition>[] value();
}
根据定义我们在用@Conditional注解时,需要指定一个或多个Condition的实现类,所以我们先来提供一个实现类:
public class MyCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return false;
}
}
很明显,我们可以在matches方法中来定义条件逻辑:
- ConditionContext:表示条件上下文,可以通过ConditionContext获取到当前的类加载器、BeanFactory、Environment环境变量对象
- AnnotatedTypeMetadata:表示当前正在进行条件判断的Bean所对应的类信息,或方法信息(比如@Bean定义的一个Bean),可以通过AnnotatedTypeMetadata获取到当前类或方法相关的信息,从而就可以拿到条件注解的信息,当然如果一个Bean上使用了多个条件注解,那么在解析过程中都可以获取到,同时也能获取Bean上定义的其他注解信息
5、AutoConfigureAfter
编辑
四、自动配置源码分析
1、Tomcat自动配置
首先我们可以发现,在spring-boot-starter-web这个starter中,其实简介的引入了spring-boot-starter-tomcat这个starter,这个spring-boot-starter-tomcat又引入了tomcat-embed-core依赖,所以只要我们项目中依赖了spring-boot-starter-web就相当于依赖了Tomcat。
然后在SpringBoot众多的自动配置类中,有一个自动配置类叫做ServletWebServerFactoryAutoConfiguration,定义为:
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
// ...
}
首先看这个自动配置类所需要的条件:
- @ConditionalOnClass(ServletRequest.class):表示项目依赖中要有ServletRequest类(server api)
- @ConditionalOnWebApplication(type = Type.SERVLET):表示项目应用类型得是SpringMVC(讲启动过程的时候就知道如何判断一个SpringBoot应用的类型了)
在上面提到的spring-boot-starter-web中,其实还间接的引入了spring-web、spring-webmvc等依赖,这就使得第二个条件满足,而对于第一个条件的ServletRequest类,虽然它是Servlet规范中的类,但是在我们所依赖的tomcat-embed-core这个jar包中是存在这个类的,这是因为Tomcat在自己的源码中把Servlet规范中的一些代码也包含进去了,比如:
编辑
这就使得ServletWebServerFactoryAutoConfiguration这个自动配置的两个条件都符合,那么Spring就能去解析它,一解析它就发现这个自动配置类Import进来了三个类:
- ServletWebServerFactoryConfiguration.EmbeddedTomcat.class
- ServletWebServerFactoryConfiguration.EmbeddedJetty.class
- ServletWebServerFactoryConfiguration.EmbeddedUndertow.class
很明显,Import进来的这三个类应该是差不多,我们看EmbeddedTomcat这个类:
@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;
}
}
可以发现这个类是一个配置类,所以Spring也会来解析它,不过它也有两个条件:
- @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class }):项目依赖中要有Servlet.class、Tomcat.class、UpgradeProtocol.class这三个类,这个条件比较容易理解,项目依赖中有Tomcat的类,那这个条件就符合。
- @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT),项目中没有ServletWebServerFactory类型的Bean,因为这个配置类的内部就是定义了一个TomcatServletWebServerFactory类型的Bean,TomcatServletWebServerFactory实现了ServletWebServerFactory接口,所以这个条件注解的意思就是,如果程序员自己没有定义ServletWebServerFactory类型的Bean,那么就符合条件,不然,如果程序员自己定义了ServletWebServerFactory类型的Bean,那么条件就不符合,也就导致SpringBoot给我们定义的TomcatServletWebServerFactory这个Bean就不会生效,最终生效的就是程序员自己定义的。
所以,通常只要我们项目依赖中有Tomcat依赖,那就符合条件,那最终Spring容器中就会有TomcatServletWebServerFactory这个Bean。
对于另外的EmbeddedJetty和EmbeddedUndertow,也差不多,都是判断项目依赖中是否有Jetty和Undertow的依赖,如果有,那么对应在Spring容器中就会存在JettyServletWebServerFactory类型的Bean、或者存在UndertowServletWebServerFactory类型的Bean。
总结一下:
- 有Tomcat依赖,就有TomcatServletWebServerFactory这个Bean
- 有Jetty依赖,就有JettyServletWebServerFactory这个Bean
- 有Undertow依赖,就有UndertowServletWebServerFactory这个Bean
那么SpringBoot给我们配置的这几个Bean到底有什么用呢?
我们前面说到,TomcatServletWebServerFactory实现了ServletWebServerFactory这个接口,这个接口的定义为:
public interface ServletWebServerFactory {
WebServer getWebServer(ServletContextInitializer... initializers);
}
public interface WebServer {
void start() throws WebServerException;
void stop() throws WebServerException;
int getPort();
}
我们发现ServletWebServerFactory其实就是用来获得WebServer对象的,而WebServer拥有启动、停止、获取端口等方法,那么很自然,我们就发现WebServer其实指的就是Tomcat、Jetty、Undertow,而TomcatServletWebServerFactory就是用来生成Tomcat所对应的WebServer对象,具体一点就是TomcatWebServer对象,并且在生成TomcatWebServer对象时会把Tomcat给启动起来,在源码中,调用TomcatServletWebServerFactory对象的getWebServer()方法时就会启动Tomcat。
我们再来看TomcatServletWebServerFactory这个Bean的定义:
@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;
}
要构造这个Bean,Spring会从Spring容器中获取到TomcatConnectorCustomizer、TomcatContextCustomizer、TomcatProtocolHandlerCustomizer这三个类型的Bean,然后把它们添加到TomcatServletWebServerFactory对象中去,很明显这三种Bean是用来配置Tomcat的,比如:
- TomcatConnectorCustomizer:是用来配置Tomcat中的Connector组件的
- TomcatContextCustomizer:是用来配置Tomcat中的Context组件的
- TomcatProtocolHandlerCustomizer:是用来配置Tomcat中的ProtocolHandler组件的
也就是我们可以通过定义TomcatConnectorCustomizer类型的Bean,来对Tomcat进行配置,比如:
@SpringBootApplication
public class MyApplication {
@Bean
public TomcatConnectorCustomizer tomcatConnectorCustomizer(){
return new TomcatConnectorCustomizer() {
@Override
public void customize(Connector connector) {
connector.setPort(8888);
}
};
}
public static void main(String[] args) {
SpringApplication.run(MyApplication.class);
}
}
这样Tomcat就会绑定8888这个端口。
有了TomcatServletWebServerFactory这个Bean之后,在SpringBoot的启动过程中,会执行ServletWebServerApplicationContext的onRefresh()方法,而这个方法会调用createWebServer()方法,而这个方法中最为重要的两行代码为:
ServletWebServerFactory factory = getWebServerFactory();
this.webServer = factory.getWebServer(getSelfInitializer());
很明显,getWebServerFactory()负责获取具体的ServletWebServerFactory对象,要么是TomcatServletWebServerFactory对象,要么是JettyServletWebServerFactory对象,要么是UndertowServletWebServerFactory对象,注意只能获取到一个,然后调用该对象的getWebServer方法,启动对应的Tomcat、或者Jetty、或者Undertow。
getWebServerFactory方法中的逻辑比较简单,获取Spring容器中的ServletWebServerFactory类型的Bean对象,如果没有获取到则抛异常,如果找到多个也抛异常,也就是在Spring容器中只能有一个ServletWebServerFactory类型的Bean对象。
拿到TomcatServletWebServerFactory对象后,就调用它的getWebServer方法,而在这个方法中就会生成一个Tomcat对象,并且利用前面的TomcatConnectorCustomizer等等会Tomcat对象进行配置,最后启动Tomcat。
这样在启动应用时就完成了Tomcat的启动,到此我们通过这个案例也看到了具体的Starter机制、自动配置的具体使用。
自动配置类ServletWebServerFactoryAutoConfiguration中,还会定义一个ServletWebServerFactoryCustomizer类型的Bean,定义为:
@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties,
ObjectProvider<WebListenerRegistrar> webListenerRegistrars,
ObjectProvider<CookieSameSiteSupplier> cookieSameSiteSuppliers) {
return new ServletWebServerFactoryCustomizer(serverProperties,
webListenerRegistrars.orderedStream().collect(Collectors.toList()),
cookieSameSiteSuppliers.orderedStream().collect(Collectors.toList()));
}
这个Bean会接收一个ServerProperties的Bean,ServerProperties的Bean对应的就是properties文件中前缀为server的配置,我们可以利用ServerProperties对象的getPort方法获取到我们所配置的server.port的值。
而ServletWebServerFactoryCustomizer是针对一个ServletWebServerFactory的自定义器,也就是用来配置TomcatServletWebServerFactory这个Bean的,到时候ServletWebServerFactoryCustomizer就会利用ServerProperties对象来对TomcatServletWebServerFactory对象进行设置。
在ServletWebServerFactoryAutoConfiguration这个自动配置上,除开Import了EmbeddedTomcat、EmbeddedJetty、EmbeddedUndertow这三个配置类,还Import了一个ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,这个BeanPostProcessorsRegistrar会向Spring容器中注册一个WebServerFactoryCustomizerBeanPostProcessor类型的Bean。
WebServerFactoryCustomizerBeanPostProcessor是一个BeanPosrtProcessor,它专门用来处理类型为WebServerFactory的Bean对象,而我们的TomcatServletWebServerFactory、JettyServletWebServerFactory、UndertowServletWebServerFactory也都实现了这个接口,所以不管当前项目依赖的情况,只要在Spring在创建比如TomcatServletWebServerFactory这个Bean时,WebServerFactoryCustomizerBeanPostProcessor就会对它进行处理,处理的逻辑为:
- 从Spring容器中拿到WebServerFactoryCustomizer类型的Bean,也就是前面说的ServletWebServerFactoryCustomizer对象
- 然后调用ServletWebServerFactoryCustomizer对象的customize方法,把TomcatServletWebServerFactory对象传入进去
- customize方法中就会从ServerProperties对象获取各种配置,然后设置给TomcatServletWebServerFactory对象
比如:
编辑
这样当TomcatServletWebServerFactory这个Bean对象创建完成后,它里面的很多属性,比如port,就已经是程序员所配置的值了,后续执行getWebServer方法时,就直接获取自己的属性,比如port属性,设置给Tomcat,然后再利用TomcatConnectorCustomizer等进行处理,最后启动Tomcat。
到此,SpringBoot整合Tomcat的核心原理就分析完了,主要涉及的东西有:
- spring-boot-starter-web:会自动引入Tomcat、SpringMVC的依赖
- ServletWebServerFactoryAutoConfiguration:自动配置类
- ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar:用来注册WebServerFactoryCustomizerBeanPostProcessor
- ServletWebServerFactoryConfiguration.EmbeddedTomcat:配置TomcatServletWebServerFactory
- ServletWebServerFactoryConfiguration.EmbeddedJetty:配置JettyServletWebServerFactory
- ServletWebServerFactoryConfiguration.EmbeddedUndertow:配置UndertowServletWebServerFactory
- ServletWebServerFactoryCustomizer:用来配置ServletWebServerFactory
- WebServerFactoryCustomizerBeanPostProcessor:是一个BeanPostProcessor,利用ServletWebServerFactoryCustomizer来配置ServletWebServerFactory
- ServletWebServerApplicationContext中的onRefresh()方法:负责启动Tomcat
2、AOP自动配置
@Configuration(proxyBeanMethods = false)
// spring.aop.auto=true时开启AOP,或者没有配置spring.aop.auto时默认也是开启
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Advice.class)
static class AspectJAutoProxyingConfiguration {
@Configuration(proxyBeanMethods = false)
// 开启AOP的注解,使用JDK动态代理
@EnableAspectJAutoProxy(proxyTargetClass = false)
// spring.aop.proxy-target-class=false时才生效
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false")
static class JdkDynamicAutoProxyConfiguration {
}
@Configuration(proxyBeanMethods = false)
// 开启AOP的注解,使用CGLIB动态代理
@EnableAspectJAutoProxy(proxyTargetClass = true)
// spring.aop.proxy-target-class=true时生效,或者没有配置spring.aop.proxy-target-class时默认也生效
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
static class CglibAutoProxyConfiguration {
}
}
@Configuration(proxyBeanMethods = false)
// 没有aspectj的依赖,但是又要使用cglib动态代理
@ConditionalOnMissingClass("org.aspectj.weaver.Advice")
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
static class ClassProxyingConfiguration {
@Bean
static BeanFactoryPostProcessor forceAutoProxyCreatorToUseClassProxying() {
return (beanFactory) -> {
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
// 注册InfrastructureAdvisorAutoProxyCreator从而开启Spring AOP
// @EnableAspectJAutoProxy会注册AnnotationAwareAspectJAutoProxyCreator,也会开启Spring AOP但是同时有用解析AspectJ注解的功能
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
};
}
}
}
3、MyBatis自动配置
Mybatis的自动配置类为MybatisAutoConfiguration,该类中配置了一个SqlSessionFactory和AutoConfiguredMapperScannerRegistrar。
SqlSessionFactory这个Bean是Mybatis需要配置的,AutoConfiguredMapperScannerRegistrar会注册并配置一个MapperScannerConfigurer。
public static class AutoConfiguredMapperScannerRegistrar
implements BeanFactoryAware, EnvironmentAware, ImportBeanDefinitionRegistrar {
private BeanFactory beanFactory;
private Environment environment;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if (!AutoConfigurationPackages.has(this.beanFactory)) {
logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.");
return;
}
logger.debug("Searching for mappers annotated with @Mapper");
// 获取AutoConfigurationPackages Bean从而获取SpringBoot的扫描路径
List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
if (logger.isDebugEnabled()) {
packages.forEach(pkg -> logger.debug("Using auto-configuration base package '{}'", pkg));
}
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
builder.addPropertyValue("processPropertyPlaceHolders", true);
// 限制了接口上得加Mapper注解
builder.addPropertyValue("annotationClass", Mapper.class);
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));
BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class);
Set<String> propertyNames = Stream.of(beanWrapper.getPropertyDescriptors()).map(PropertyDescriptor::getName)
.collect(Collectors.toSet());
if (propertyNames.contains("lazyInitialization")) {
// Need to mybatis-spring 2.0.2+
builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}");
}
if (propertyNames.contains("defaultScope")) {
// Need to mybatis-spring 2.0.6+
builder.addPropertyValue("defaultScope", "${mybatis.mapper-default-scope:}");
}
// for spring-native
boolean injectSqlSession = environment.getProperty("mybatis.inject-sql-session-on-mapper-scan", Boolean.class,
Boolean.TRUE);
if (injectSqlSession && this.beanFactory instanceof ListableBeanFactory) {
ListableBeanFactory listableBeanFactory = (ListableBeanFactory) this.beanFactory;
Optional<String> sqlSessionTemplateBeanName = Optional
.ofNullable(getBeanNameForType(SqlSessionTemplate.class, listableBeanFactory));
Optional<String> sqlSessionFactoryBeanName = Optional
.ofNullable(getBeanNameForType(SqlSessionFactory.class, listableBeanFactory));
if (sqlSessionTemplateBeanName.isPresent() || !sqlSessionFactoryBeanName.isPresent()) {
builder.addPropertyValue("sqlSessionTemplateBeanName",
sqlSessionTemplateBeanName.orElse("sqlSessionTemplate"));
} else {
builder.addPropertyValue("sqlSessionFactoryBeanName", sqlSessionFactoryBeanName.get());
}
}
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
}
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
private String getBeanNameForType(Class<?> type, ListableBeanFactory factory) {
String[] beanNames = factory.getBeanNamesForType(type);
return beanNames.length > 0 ? beanNames[0] : null;
}
}