SpringBoot自动配置原理

SpringBoot自动配置主要经过@EnableAutoConfiguration, @Conditional, @EnableConfigurationProperties或者@ConfigurationProperties 等几个注解来进行自动配置完成的。html

@EnableAutoConfiguration开启自动配置,主要做用就是调用Spring-Core包里的loadFactoryNames(),将autoconfig 包里的已经写好的自动配置加载进来。web

@Conditional条件注解,经过判断类路径下有没有相应配置的jar包来肯定是否加载和自动配置这个类。spring

@EnableConfigurationProperties的做用就是,给自动配置提供具体的配置参数,只须要写在 application.properties 中,就能够经过映射写入配置类的POJO属性中。bootstrap

@EnableAutoConfiguration

@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应用时,才会执行

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

相关文章
相关标签/搜索