SpringBoot自動配置主要經過@EnableAutoConfiguration, @Conditional, @EnableConfigurationProperties或者@ConfigurationProperties 等幾個註解來進行自動配置完成的。html
@EnableAutoConfiguration開啓自動配置,主要做用就是調用Spring-Core包裏的loadFactoryNames(),將autoconfig 包裏的已經寫好的自動配置加載進來。web
@Conditional條件註解,經過判斷類路徑下有沒有相應配置的jar包來肯定是否加載和自動配置這個類。spring
@EnableConfigurationProperties的做用就是,給自動配置提供具體的配置參數,只須要寫在 application.properties 中,就能夠經過映射寫入配置類的POJO屬性中。bootstrap
@Enable*註解並非SpringBoot新發明的註解,Spring 3框架就引入了這些註釋,用這些註釋替代XML配置文件。好比:
@EnableTransactionManagement註解,它可以聲明事務管理
@EnableWebMvc註解,它能啓用Spring MVC
@EnableScheduling註解,它能夠初始化一個調度器。app
這些註釋事實上都是簡單的配置,經過@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導入了AutoConfigurationImportSelectorless
@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文件。ide
@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將查詢配置文件命名的屬性。ui
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.factoriesthis
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應用時,纔會執行
@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