SpringBoot自動配置主要通過@EnableAutoConfiguration, @Conditional, @EnableConfigurationProperties或者@ConfigurationProperties 等幾個注解來進行自動配置完成的。
@EnableAutoConfiguration開啟自動配置,主要作用就是調用Spring-Core包里的loadFactoryNames(),將autoconfig 包里的已經寫好的自動配置加載進來。
@Conditional條件注解,通過判斷類路徑下有沒有相應配置的jar包來確定是否加載和自動配置這個類。
@EnableConfigurationProperties的作用就是,給自動配置提供具體的配置參數,只需要寫在 application.properties 中,就可以通過映射寫入配置類的POJO屬性中。
@EnableAutoConfiguration
@Enable*注解並不是SpringBoot新發明的注解,Spring 3框架就引入了這些注釋,用這些注釋替代XML配置文件。比如:
@EnableTransactionManagement注解,它能夠聲明事務管理
@EnableWebMvc注解,它能啟用Spring MVC
@EnableScheduling注解,它可以初始化一個調度器。
這些注釋事實上都是簡單的配置,通過@Import注解導入。
從啟動類的@SpringBootApplication進入,在里面找到了@EnableAutoConfiguration,
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication {}
@EnableAutoConfiguration里通過@Import導入了AutoConfigurationImportSelector
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {}
找到selectImports()方法,他調用了getCandidateConfigurations()方法,在這里,這個方法又調用了Spring Core包中的loadFactoryNames()方法。這個方法的作用是,會查詢META-INF/spring.factories文件中包含的JAR文件。
@Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry( autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }
protected AutoConfigurationEntry getAutoConfigurationEntry( 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); return new AutoConfigurationEntry(configurations, exclusions); }
當找到spring.factories文件后,SpringFactoriesLoader將查詢配置文件命名的屬性。
protected List<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; }
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); } private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = cache.get(classLoader); if (result != null) { return result; } try { Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); result = new LinkedMultiValueMap<>(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { String factoryClassName = ((String) entry.getKey()).trim(); for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { result.add(factoryClassName, factoryName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); }
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
org.springframework.boot.autoconfigure的spring.factories

spring.factories文件中,可以看到一系列Spring Boot自動配置的列表
下面我們來看spring.factories文件自動配置Kafka的細節,KafkaAutoConfiguration:
@Configuration @ConditionalOnClass(KafkaTemplate.class) @EnableConfigurationProperties(KafkaProperties.class) @Import({ KafkaAnnotationDrivenConfiguration.class, KafkaStreamsAnnotationDrivenConfiguration.class }) public class KafkaAutoConfiguration { private final KafkaProperties properties; private final RecordMessageConverter messageConverter; public KafkaAutoConfiguration(KafkaProperties properties, ObjectProvider<RecordMessageConverter> messageConverter) { this.properties = properties; this.messageConverter = messageConverter.getIfUnique(); } @Bean @ConditionalOnMissingBean(KafkaTemplate.class) public KafkaTemplate<?, ?> kafkaTemplate( ProducerFactory<Object, Object> kafkaProducerFactory, ProducerListener<Object, Object> kafkaProducerListener) { KafkaTemplate<Object, Object> kafkaTemplate = new KafkaTemplate<>( kafkaProducerFactory); if (this.messageConverter != null) { kafkaTemplate.setMessageConverter(this.messageConverter); } kafkaTemplate.setProducerListener(kafkaProducerListener); kafkaTemplate.setDefaultTopic(this.properties.getTemplate().getDefaultTopic()); return kafkaTemplate; } @Bean @ConditionalOnMissingBean(ProducerListener.class) public ProducerListener<Object, Object> kafkaProducerListener() { return new LoggingProducerListener<>(); } @Bean @ConditionalOnMissingBean(ConsumerFactory.class) public ConsumerFactory<?, ?> kafkaConsumerFactory() { return new DefaultKafkaConsumerFactory<>( this.properties.buildConsumerProperties()); } @Bean @ConditionalOnMissingBean(ProducerFactory.class) public ProducerFactory<?, ?> kafkaProducerFactory() { DefaultKafkaProducerFactory<?, ?> factory = new DefaultKafkaProducerFactory<>( this.properties.buildProducerProperties()); String transactionIdPrefix = this.properties.getProducer() .getTransactionIdPrefix(); if (transactionIdPrefix != null) { factory.setTransactionIdPrefix(transactionIdPrefix); } return factory; } @Bean @ConditionalOnProperty(name = "spring.kafka.producer.transaction-id-prefix") @ConditionalOnMissingBean public KafkaTransactionManager<?, ?> kafkaTransactionManager( ProducerFactory<?, ?> producerFactory) { return new KafkaTransactionManager<>(producerFactory); } @Bean @ConditionalOnProperty(name = "spring.kafka.jaas.enabled") @ConditionalOnMissingBean public KafkaJaasLoginModuleInitializer kafkaJaasInitializer() throws IOException { KafkaJaasLoginModuleInitializer jaas = new KafkaJaasLoginModuleInitializer(); Jaas jaasProperties = this.properties.getJaas(); if (jaasProperties.getControlFlag() != null) { jaas.setControlFlag(jaasProperties.getControlFlag()); } if (jaasProperties.getLoginModule() != null) { jaas.setLoginModule(jaasProperties.getLoginModule()); } jaas.setOptions(jaasProperties.getOptions()); return jaas; } @Bean @ConditionalOnMissingBean public KafkaAdmin kafkaAdmin() { KafkaAdmin kafkaAdmin = new KafkaAdmin(this.properties.buildAdminProperties()); kafkaAdmin.setFatalIfBrokerNotAvailable(this.properties.getAdmin().isFailFast()); return kafkaAdmin; } }
這個類進行了簡單的Spring配置,聲明了Kafka所需典型Bean,和其它很多類一樣,重度依賴於Spring Boot注釋:
1)@ConditionOnClass激活一個配置,當類路徑中存在這個類時才會配置該類
2)@EnableConfigurationProperties自動映射一個POJO到Spring Boot配置文件(默認是application.properties文件)的屬性集。
3)@ConditionalOnMissingBean啟用一個Bean定義,但必須是這個Bean之前未定義過才有效。
還可以使用@ AutoConfigureBefore注釋、@AutoConfigureAfter注釋來定義這些配置類的載入順序。
着重了解@Conditional注釋,Spring 4框架的新特性
此注釋使得只有在特定條件滿足時才啟用一些配置。SrpingBoot的AutoConfig大量使用了@Conditional,它會根據運行環境來動態注入Bean。這里介紹一些@Conditional的使用和原理,並自定義@Conditional來自定義功能。
@Conditional是SpringFramework的功能,SpringBoot在它的基礎上定義了
@ConditionalOnClass,@ConditionalOnProperty等一系列的注解來實現更豐富的內容。
具體幾個@Conditon*注解的含義
@ConditionalOnBean
僅僅在當前上下文中存在某個對象時,才會實例化一個Bean
@ConditionalOnClass
某個class位於類路徑上,才會實例化一個Bean),該注解的參數對應的類必須存在,否則不解析該注解修飾的配置類
@ConditionalOnExpression
當表達式為true的時候,才會實例化一個Bean
@ConditionalOnMissingBean
僅僅在當前上下文中不存在某個對象時,才會實例化一個Bean,該注解表示,如果存在它修飾的類的bean,則不需要再創建這個bean,可以給該注解傳入參數例如@ConditionOnMissingBean(name = "example"),這個表示如果name為“example”的bean存在,這該注解修飾的代碼塊不執行
@ConditionalOnMissingClass
某個class類路徑上不存在的時候,才會實例化一個Bean
@ConditionalOnNotWebApplication
不是web應用時,才會執行
Properties系列注釋
@EnableConfigurationProperties
@ConfigurationProperties(prefix = "may")
在需要注入配置的類上加上這個注解,prefix的意思是,以該前綴打頭的配置
如果不用系統初始的application.properties配置類,而是使用自己的如winner.properties,可以如下配置
/** * @PropertySource 只能加載.properties文件 * @author winner_0715 */ @Configuration @PropertySource("classpath:winner.properties") @ConfigurationProperties(prefix = "winner") public class WinnerConfig { private String name; private String email; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } }
最后注意在spring Boot入口類加上@EnableConfigurationProperties
@ConfigurationProperties(prefix = "spring.kafka") public class KafkaProperties { /** * Comma-delimited list of host:port pairs to use for establishing the initial * connections to the Kafka cluster. Applies to all components unless overridden. */
private List<String> bootstrapServers = new ArrayList<>( Collections.singletonList("localhost:9092")); /** * ID to pass to the server when making requests. Used for server-side logging. */
private String clientId; /** * Additional properties, common to producers and consumers, used to configure the * client. */
private final Map<String, String> properties = new HashMap<>(); private final Consumer consumer = new Consumer();
Ref:
https://www.cnblogs.com/leihuazhe/p/7743479.html
