@EnableAutoConfiguration原理
首先我们得知道@EnableAutoConfiguration作用,官方是这么解释的
/*
启用 Spring Application Context 的自动配置,尝试猜测和配置您可能需要的 bean。自动配置类通常根据您的类路径和您定义的 bean 应用。例如,如果您的类路径中有tomcat-embedded.jar ,您可能需要一个TomcatServletWebServerFactory (除非您已经定义了自己的ServletWebServerFactory bean)。
使用SpringBootApplication时,上下文的自动配置会自动启用,因此添加此注解不会产生额外的影响。
自动配置尝试尽可能智能,并且会随着您定义更多自己的配置而后退。您始终可以手动exclude()任何您不想应用的配置(如果您无权访问它们,请使用excludeName() )。您还可以通过spring.autoconfigure.exclude属性排除它们。自动配置总是在用户定义的 bean 注册后应用。
使用@EnableAutoConfiguration注释的类的包,通常通过@SpringBootApplication ,具有特定的意义,通常用作“默认值”。例如,它将在扫描@Entity类时使用。通常建议您将@EnableAutoConfiguration (如果您不使用@SpringBootApplication )放在根包中,以便可以搜索所有子包和类。
自动配置类是常规的 Spring Configuration bean。它们使用SpringFactoriesLoader机制定位(针对此类)。通常自动配置 bean 是@Conditional bean(最常使用@ConditionalOnClass和@ConditionalOnMissingBean注释)。
*/@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class)public@interfaceEnableAutoConfiguration{String ENABLED_OVERRIDE_PROPERTY ="spring.boot.enableautoconfiguration";/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/Class<?>[]exclude()default{};/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/String[]excludeName()default{};}
大体意思就是用来自动装配所需的Bean的,这也是我们在开发过程为什么,只需要引入一个starter包,我们能从IOC容器中直接获取到相应的包的。当如,如果我们想引入自己的starter,我们也可以定义自己的@Enablexxx注解。
1、模块装配
要弄懂@EnableAutoConfiguration,首先我们需要知道Spring模块装配的知识,在我们开发过程中,经常会用到@Enablexxx注解,例如@EnableFeignClients、@EnableScheduling、@EnableAsync等,特别是在整合第三方工具包时。
那么@Enablexxx注解到底是如何起作用的呢?为什么能起到开关的作用的呢?
当我们点进注解时会发现,每一个注解上都有个@Import注解,这个注解就是用于我们开头说的Spring的模块装配的
1.1、@Import注解
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic@interfaceImport{/**
* {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
* or regular component classes to import.
*/Class<?>[]value();}
@Import注解有个value属性,用于存放需要注入的Bean,value可以是以下值
- 普通的类,当注入普通类时,该类就会被注入到IOC容器中
- 继承了
ImportSelector
接口的类(用的较多)publicinterfaceImportSelector{/** * Select and return the names of which class(es) should be imported based on * the {@link AnnotationMetadata} of the importing @{@link Configuration} class. */String[]selectImports(AnnotationMetadata importingClassMetadata);}
该接口返回了String[]数组
,该数组返回的应该类的全限定名数组,通过全限定名,可以将这些类注入到IOC容器中。 - 集成
ImportBeanDefinitionRegistrar
的类,与ImportSelector不同的是,该接口的方法并不会返回值,我们操作的是BeanDefinition,我们可以使用第二个形参registry
导BeanDefinitionpublicinterfaceImportBeanDefinitionRegistrar{/** * Register bean definitions as necessary based on the given annotation metadata of * the importing {@code @Configuration} class. * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be * registered here, due to lifecycle constraints related to {@code @Configuration} * class processing. * @param importingClassMetadata annotation metadata of the importing class * @param registry current bean definition registry */publicvoidregisterBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry);}
2、Spring SPI机制
导入的机制我们了解了,现在我们需要知道,springboot是如何做到我们引入第三方starter时,自动导入相关的Bean到IOC容器的呢?这其实就用到了Spring的SPI机制。
SPI全称是Service Provider Interface 服务提供接口,它可以通过一个指定的接口 / 抽象类,寻找到预先配置好的实现类(并创建实现类对象)
2.1、声明SPI文件
SpringFramework 的 SPI 文件也是有规矩的,它需要放在工程的 **
META-INF
** 下,且文件名必须为 **
spring.factories
** 。而文件的内容,其实就是一个
properties
:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
2.2、测试获取
publicstaticvoidmain(String[] args)throwsException{// 加载并实例化List<DemoDao> demoDaos =SpringFactoriesLoader.loadFactories(DemoDao.class,SpringSpiApplication.class.getClassLoader());
demoDaos.forEach(dao ->{System.out.println(dao);});System.out.println("------------------------------------------------");// 只加载全限定类名List<String> daoClassNames =SpringFactoriesLoader.loadFactoryNames(DemoDao.class,SpringSpiApplication.class.getClassLoader());
daoClassNames.forEach(className ->{System.out.println(className);});}------------------------------------------------------------------------com.linkedbear.spring.configuration.z_spi.bean.DemoMySQLDaoImpl@7506e922com.linkedbear.spring.configuration.z_spi.bean.DemoOracleDaoImpl@4ee285c6------------------------------------------------com.linkedbear.spring.configuration.z_spi.bean.DemoMySQLDaoImplcom.linkedbear.spring.configuration.z_spi.bean.DemoOracleDaoImpl
3、@EnableAutoConfiguration原理
@EableAutoConfiguration底层就是就是运用了@Import+SPI的方式实现了starter包的自动装配,接下来我们就来验证一下
我们@Import注解中的
AutoConfigurationImportSelector.class
类
publicclassAutoConfigurationImportSelectorimplementsDeferredImportSelector,BeanClassLoaderAware,ResourceLoaderAware,BeanFactoryAware,EnvironmentAware,Ordered{privatestaticfinalAutoConfigurationEntry EMPTY_ENTRY =newAutoConfigurationEntry();privatestaticfinalString[] NO_IMPORTS ={};privatestaticfinalLog logger =LogFactory.getLog(AutoConfigurationImportSelector.class);privatestaticfinalString PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE ="spring.autoconfigure.exclude";privateConfigurableListableBeanFactory beanFactory;privateEnvironment environment;privateClassLoader beanClassLoader;privateResourceLoader resourceLoader;@OverridepublicString[]selectImports(AnnotationMetadata annotationMetadata){if(!isEnabled(annotationMetadata)){return NO_IMPORTS;}AutoConfigurationMetadata autoConfigurationMetadata =AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);AutoConfigurationEntry autoConfigurationEntry =getAutoConfigurationEntry(
autoConfigurationMetadata, annotationMetadata);returnStringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}/**
* Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata}
* of the importing {@link Configuration @Configuration} class.
* @param autoConfigurationMetadata the auto-configuration metadata
* @param annotationMetadata the annotation metadata of the configuration class
* @return the auto-configurations that should be imported
*/protectedAutoConfigurationEntrygetAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,AnnotationMetadata annotationMetadata){if(!isEnabled(annotationMetadata)){return EMPTY_ENTRY;}AnnotationAttributes attributes =getAttributes(annotationMetadata);List<String> configurations =getCandidateConfigurations(annotationMetadata,
attributes);
configurations =removeDuplicates(configurations);Set<String> exclusions =getExclusions(annotationMetadata, attributes);checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations =filter(configurations, autoConfigurationMetadata);fireAutoConfigurationImportEvents(configurations, exclusions);returnnewAutoConfigurationEntry(configurations, exclusions);}...}
我们看到
AutoConfigurationImportSelector.class
实现了
DeferredImportSelector
的接口
publicinterfaceDeferredImportSelectorextendsImportSelector{/**
* Return a specific import group or {@code null} if no grouping is required.
* @return the import group class or {@code null}
*/@NullabledefaultClass<?extendsGroup>getImportGroup(){returnnull;}}
而
DeferredImportSelector
接口实现了
ImportSelector
接口,所以使用了上述的第二种方式。
然后我们来看
selectImports
中的具体逻辑
protectedAutoConfigurationEntrygetAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,AnnotationMetadata annotationMetadata){if(!isEnabled(annotationMetadata)){return EMPTY_ENTRY;}AnnotationAttributes attributes =getAttributes(annotationMetadata);List<String> configurations =getCandidateConfigurations(annotationMetadata,
attributes);
configurations =removeDuplicates(configurations);Set<String> exclusions =getExclusions(annotationMetadata, attributes);checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations =filter(configurations, autoConfigurationMetadata);fireAutoConfigurationImportEvents(configurations, exclusions);returnnewAutoConfigurationEntry(configurations, exclusions);}
其中
cofigurations
是通过
getCandidateConfigurations
方法来获取的,跟进去看
protectedList<String>getCandidateConfigurations(AnnotationMetadata metadata,AnnotationAttributes attributes){List<String> configurations =SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());Assert.notEmpty(configurations,"No auto configuration classes found in META-INF/spring.factories. If you "+"are using a custom packaging, make sure that file is correct.");return configurations;}
这里就很清楚了,通过SPI机制获取到spring.factories中
EnableAutoConfiguration
对应的全限定名
这时,我们打开
spring-boot-autoconfigure-2.1.3.RELEASE.jar!\META-INF\spring.factories
文件
# 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.cloud.CloudServiceConnectorsAutoConfiguration,\
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.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.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.elasticsearch.rest.RestClientAutoConfiguration,\
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.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.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.ReactiveWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
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
原来springboot已经帮我们把相关的需要导入的starter的配置类写好并到写到了spring.factories文件中!
@EnableAutoConfiguration的原理大概如此,大家也可以自己通过@Import+SPI实现自己的模块装配哦!
版权归原作者 吃饭睡觉撸代码 所有, 如有侵权,请联系我们删除。