引言
大家都知道SpringBoot简化了Spring开发工作,让开发者不用再去面对繁琐的配置,可以使我们可以迅速上手进行开发,将重点放在业务逻辑的实现上。但也正因为这样,使得开发者容易忽略对于其背后原理的理解。我们可能知道怎么用,但是实际上并不知道SpringBoot如何实现自动配置以及如何通过内置tomcat进行启动等等的原理。为了探究SpringBoot背后的技术原理,特地将学习的过程记录下来形成一个文章系列,另外希望对这方面有相同困惑的同学有所裨益。
自动配置介绍
Kafka自动配置源码分析
总结
一、自动配置介绍
我们都知道,在没有SpringBoot
之前,利用Spring
进行开发的时候,研发需要花费大量精力去定义模板化的各类配置文件。Spring
最初使用Bean Factory以及动态代理实现各模块之间的解耦,它通过配置文件将bean扫描到Spring容器中。而SpringBoot将这种xml解析配置的过程,通过注解自动配置的方式来进行替换,它根据定义在classpath下的类,自动生成对应的bean,同时将其加载到Spring的context中。SpringBoot通过条件化配置来启动某个能力项。
在SpringBoot启动类WebApplication中,可以看到很多个注解。我们知道SpringBoot项目是高度依赖注解的,它可以在main函数中启动整个应用。
@SpringBootApplication(scanBasePackages = {"com.test"}) @MapperScan("com.test.module.mapper") @ImportResource(locations = {"classpath:springMVC-servlet.xml"}) @ServletComponentScan public class WebApplication extends SpringBootServletInitializer{ static Logger logger = LoggerFactory.getLogger(WebApplication.class); public static void main(String[] args) { SpringApplication.run(WebApplication.class, args); } }
以上代码中,@SpringBootApplication是SpringBoot的核心注解,它是一系列注解的集合。它对应的源码如下所示。在这些注解当中@EnableAutoConfiguration即为当前的项目提供自动配置功能,它也是一系列注解的集合。该注解可以让Spring Boot根据类路径中的jar包依赖为当前项目进行自动配置。
@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 { @AliasFor(annotation = EnableAutoConfiguration.class) Class<?>[] exclude() default {}; @AliasFor(annotation = EnableAutoConfiguration.class) String[] excludeName() default {}; @AliasFor(annotation = ComponentScan.class, attribute = "basePackages") String[] scanBasePackages() default {}; @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses") Class<?>[] scanBasePackageClasses() default {}; }
通过注解的方式实现配置的自动化,主要在spring-boot-autoconfigure-1.4.3.RELEASE-sources.jar这个jar包中提供了对于SpringBoot自动化配置的支持。这个jar包中包含了如下包,篇幅有限只列出了部分包。
在这个jar包中的META-INF文件夹中,可以看到spring.factories文件
在spring.factories文件中我们看到了一些初始化的类、监听器以及构建类等等。
我们具体看一下@EnableAutoConfiguration这个注解里面的内容。Spring中有很多@Enable-*开头的注解,类似@EnableScheduling以及@EnableCaching等等,这类注解即为该修饰的类赋予某项能力,在每个该类注解中都会通过@Import注解来导入实现对应功能的类。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(EnableAutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { 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 {}; }
在该注解中引入了EnableAutoConfigurationImportSelector
这个类,按照这个类的字面理解为自动配置导入选择器,它实现了以下几个接口。
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered{ ...... }
在这个类中,使用SpringFactoriesLoader.loadFactoryNames方法来扫描具有META-INF/spring.factories文件的jar包,spring-boot-autoconfigure-x.x.x.x.jar里就有一个spring.factories文件,这个文件中声明了有哪些类要自动配置。
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; }
二、Kafka自动配置源码分析
下面分析下Kafka自动配置类,贴上其源码,相关源码已经加上注释。
//配置注解 @Configuration //KafkaTemplate类在classpath目录下存在时,才会去解析KafkaAutoConfiguration自动配置类 @ConditionalOnClass(KafkaTemplate.class) //自动注入属性,如果在application.properties配置文件中定义,则会将配置文件中key对应的value值注入到KafkaProperties中 @EnableConfigurationProperties(KafkaProperties.class) //导入KafkaAnnotationDrivenConfiguration @Import(KafkaAnnotationDrivenConfiguration.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(); } //向Spring容器注入bean @Bean //在上下文中没有KafkaTemplate时,才会实例化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; } }
三、总结
将SpringBoot自动配置过程用流程图进行表示,更能形象化的理解自动配置的流程。