spring-boot-autoconfigure模块用法详解
作者:五克松
初次接触spring-boot的时候,我们经常会看到这样的文章:“为什么要使用spring-boot” "spring-boot与spring mvc有什么区别",在这些文章中几乎都会出现这样的一句话“约定优于配置”,确实是这样的,spring-boot与spring-mvc的一个重要区别就是spring-boot遵循“约定优于配置”这一原则,而spring-boot-autoconfigure模块正是完美的实现这个原则,所以我们今天就来说一说spring-boot-autoconfigure这个模块。
顾名思义,autoconfigure就是自动配置的意思,我们先看一个比较常用的例子,我们都知道,对于90%的web项目我们都需要使用数据库(比如mysql),所以就需要用到datasource,我们就先以datasource的自动配置为例吧。
使用过spring-boot的都知道,我们只需要在application.yml中进行以下配置即可在应用中使用对应的数据库连接
spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: "jdbc:mysql://127.0.0.1/crs_db?useUnicode=true&characterEncoding=utf" username: root password: 123456
对于习惯了spring-mvc的同学来说,这真是太简洁了!我们下面来看一下spring-boot-autoconfigure是怎样简化这些步骤的,话不多说,直奔关键代码: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
@Configuration // 备注1 @ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) //备注2 @EnableConfigurationProperties(DataSourceProperties.class) //备注3 @Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class }) //备注4 public class DataSourceAutoConfiguration { // 这里是为了开启一个内嵌的datasource,因为不满足某些条件,因此最终不会实例化一个dataSource @Configuration @Conditional(EmbeddedDatabaseCondition.class) @ConditionalOnMissingBean({ DataSource.class, XADataSource.class }) @Import(EmbeddedDataSourceConfiguration.class) protected static class EmbeddedDatabaseConfiguration { } @Configuration @Conditional(PooledDataSourceCondition.class) // 由于上面的EmbeddedDatabase没有实例化成功,所以在这里还没有dataSource,所以还可以往下玩 @ConditionalOnMissingBean({ DataSource.class, XADataSource.class }) @Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class, DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class }) // 备注5 protected static class PooledDataSourceConfiguration { } // 其它代码省略
备注1: @Configuration 这个注解我就不解释了,用过spring-boot的都知道是干啥的
备注2: @ConditionalOnClass表示只有classpath中能找到DataSource.class和EmbeddedDatabaseType.class时,DataSourceAutoConfiguration这个类才会被spring容器实例化,这其实也解释了为什么我们有时候只需要在pom.xml添加某些依赖包,某些功能就自动打开了,就是这个注解干的好事儿。
备注3: @EnableConfigurationProperties引入了DataSourceProperties的配置,简单看一下代码,是不是有一种似曾相识的感觉, spring.datasource.url这些不就是application.yml中的配置吗,所以从这里我们就知道我们的配置是用在这里的。
@ConfigurationProperties(prefix = "spring.datasource") public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean { private ClassLoader classLoader; private String name; private boolean generateUniqueName; private Class<? extends DataSource> type; private String driverClassName; private String url; private String username; private String password; private String jndiName; private DataSourceInitializationMode initializationMode = DataSourceInitializationMode.EMBEDDED; private String platform = "all"; private List<String> schema; private String schemaUsername; private String schemaPassword; private List<String> data; private String dataUsername; private String dataPassword; private boolean continueOnError = false; private String separator = ";"; private Charset sqlScriptEncoding;
备注4: 使用了 @Import注解引入了DataSourcePoolMetadataProvidersConfiguration 和DataSourceInitializationConfiguration,这里我们重点关注一下DataSourceInitializationConfiguration
@Configuration public class DataSourcePoolMetadataProvidersConfiguration { // tomcat自带的datasource连接池,由于默认情况下spring-boot未引入org.apache.tomcat.jdbc.pool.DataSource对应的依赖包, // 根据@ConditionalOnClass注解的作用,这个配置不会生效 @Configuration @ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class) static class TomcatDataSourcePoolMetadataProviderConfiguration { @Bean public DataSourcePoolMetadataProvider tomcatPoolDataSourceMetadataProvider() { return (dataSource) -> { if (dataSource instanceof org.apache.tomcat.jdbc.pool.DataSource) { return new TomcatDataSourcePoolMetadata( (org.apache.tomcat.jdbc.pool.DataSource) dataSource); } return null; }; } } // hikaricp是spring-boot默认的连接池(据说速度超快),HikariDataSource的包默认在spring-boot的全家桶中, //所以最终会实例化DataSourcePoolMetadataProvider 的一个bean @Configuration @ConditionalOnClass(HikariDataSource.class) static class HikariPoolDataSourceMetadataProviderConfiguration { @Bean public DataSourcePoolMetadataProvider hikariPoolDataSourceMetadataProvider() { return (dataSource) -> { if (dataSource instanceof HikariDataSource) { return new HikariDataSourcePoolMetadata( (HikariDataSource) dataSource); } return null; }; } } // dbcp2连接池的配置,很不幸,BasicDataSource不在默认包中,所以这个连接池不会生效 @Configuration @ConditionalOnClass(BasicDataSource.class) static class CommonsDbcp2PoolDataSourceMetadataProviderConfiguration { @Bean public DataSourcePoolMetadataProvider commonsDbcp2PoolDataSourceMetadataProvider() { return (dataSource) -> { if (dataSource instanceof BasicDataSource) { return new CommonsDbcp2DataSourcePoolMetadata( (BasicDataSource) dataSource); } return null; }; } } }
从上面的代码可以知道spring-boot默认实现了hikari连接池。
备注5: @Import引入了DataSourceConfiguration的几个内部类(Hikari、Tomcat、Dbcp2、Generic) ,上面我们也说了Hikari是亲儿子,所以Tomcat和Dbcp2肯定是死在了@ConditionalOnClass上了,所以我们只看Hikari和Generic
abstract class DataSourceConfiguration { //HikariDataSource肯定存在,往下走 @ConditionalOnClass(HikariDataSource.class) // 在此之前还没有实例化DataSource,往下走(如果我们在应用中主动实现了datasource,那默认的hikari就不会实现, //这个也很常见,比如我们使用阿里巴巴的durid连接池,也是基于类似的原理,当然比这要稍微复杂些) @ConditionalOnMissingBean(DataSource.class) // 当配置 spring.datasource.type=com.zaxxer.hikari.HikariDataSource或该配置不存在时,往下走 @ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource", matchIfMissing = true) static class Hikari { // 若以上4个注解的条件都满足,则在这里实例化一个HikariDataSource的bean,很显然它也是DataSource的子类 @Bean @ConfigurationProperties(prefix = "spring.datasource.hikari") public HikariDataSource dataSource(DataSourceProperties properties) { HikariDataSource dataSource = createDataSource(properties, HikariDataSource.class); if (StringUtils.hasText(properties.getName())) { dataSource.setPoolName(properties.getName()); } return dataSource; } } // 若DataSource没有bean存在,则往下走,很不幸,在上面已经实例化过HikariDataSource,止步于此, // 看去来这只是为了避免之前都没有实例化dataSource,这里来默认一个dataSource @ConditionalOnMissingBean(DataSource.class) @ConditionalOnProperty(name = "spring.datasource.type") static class Generic { @Bean public DataSource dataSource(DataSourceProperties properties) { return properties.initializeDataSourceBuilder().build(); } }
至此,数据源和连接池都已实例化完成。讲解的非常粗糙,但是这不重要,因为我们今天的主角可不是dataSource,别忘了我们要讲的是spring-boot-autoconfigure,通过上面的例子,其实最关键的是几个注解
@ConditionalOnClass : 当对应的类存在时则表示满足条件,一个典型的例子,spring-boot中经常会在你添加了某一个模块的maven依赖之后,该功能就自动开启了,比如添加org.flywaydb.flyway-core的依赖包后会默认开启flyway。
@Import : 类似于spring-mvc的import标签
@ConditionalOnMissingBean: 当对应的bean在beanFactory不存在时表示满足条件,一般用于只允许类的一个bean存在
@ConditionalOnBean: 与ConditionalOnMissingBean的作用相反,一般用于依赖的bean存在时才会实例化
@ConditionalOnProperty: 当对应的属性值为指定值时表示满足条件
因此我们在接触到一个新的模块后,如果spring-boot会自动配置它,那么我们可以在spring-boot-autoconfigure模块下找到对应的包,然后找到以AutoConfiguration结尾的类,比如flyway的FlywayAutoConfiguration,cassandra的CassandraAutoConfiguration等等,然后再配合上面的几个注解,就能很快了解新模块的加载过程。
有些爱思考的同学就要问了,你怎么知道要去找以AutoConfiguration结尾的类,这个就引入了另一个话题,spring-boot是如何实现自动配置的,它怎么知道需要加载哪些AutoConfiguration,这个其实是用到了spring-factories机制,关于spring-factories的详细原理需要单独写一篇文章来介绍,这里只是简单说一下它在autoconfigure中的应用,我们直接来看一下spring-boot-autoconfigure-2.0.6.RELEASE.jar包下的spring.factories文件
里面有这样的一部分配置(如下),key是org.springframework.boot.autoconfigure.EnableAutoConfiguration,value是各个以逗号隔开的*AutoConfiguration,结合spring-factories的运行原理,我们就可以知道所有的自动配置是从这里开始加载的。
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.cloud.CloudAutoConfiguration,\ org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\ 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.ElasticsearchAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration,\ 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.Neo4jRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\ 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.jest.JestAutoConfiguration,\ org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\ org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\ 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.groovy.template.GroovyTemplateAutoConfiguration,\ 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.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.orm.jpa.HibernateJpaAutoConfiguration,\ org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\ org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.SecurityRequestMatcherProviderAutoConfiguration,\ 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.sendgrid.SendGridAutoConfiguration,\ org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientAutoConfiguration,\ org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\ 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.ReactiveWebServerFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\ 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
知道了原理后,我们也可以自己定义自己的自动配置模块,只需要在自己的jar包下添加 MATA-INF/spring.factories文件,然后加上配置org.springframework.boot.autoconfigure.EnableAutoConfiguration=[我们自己写的autoconfigure],举个常用的例子,上面我们提到了阿里巴巴的durid数据库连接池,durid也专门提供了自动配置支持
其中spring.factories中的内容是这样的
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
DruidDataSourceAutoConfigure.java中的内容如下,跟我们之前介绍的dataSource的例子差不多,都是使用了几个常用的注解来实现自动配置。
总结一下,spring-boot通过spring-boot-autoconfigure体现了"约定优于配置"这一设计原则,而spring-boot-autoconfigure主要用到了spring.factories和几个常用的注解条件来实现自动配置,思路很清晰也很简单。
但是我特别偏爱spring-boot-autoconfigure这个模块,其中一个最大的好处就是,当我在使用一个新的功能或模块时,比如spring内置的flyway,cassandra或是阿里巴巴第三方的durid,我只需要顺着spring-boot-autoconfigure的自动配置,就能大致了解新模块的加载过程以及运行的原理,这对于我们熟悉新的模块确实起到了事半功倍的效果,希望spring-boot-autoconfigure也能给各位带来更好的学习体验。
到此这篇关于spring-boot-autoconfigure的文章就介绍到这了,更多相关spring-boot-autoconfigure内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!