SpringBoot源码分析系列之一:配置自动化

引言
大家都知道SpringBoot简化了Spring开发工作,让开发者不用再去面对繁琐的配置,可以使我们可以迅速上手进行开发,将重点放在业务逻辑的实现上。但也正因为这样,使得开发者容易忽略对于其背后原理的理解。我们可能知道怎么用,但是实际上并不知道SpringBoot如何实现自动配置以及如何通过内置tomcat进行启动等等的原理。为了探究SpringBoot背后的技术原理,特地将学习的过程记录下来形成一个文章系列,另外希望对这方面有相同困惑的同学有所裨益。

  • 自动配置介绍
  • Kafka自动配置源码分析
  • 总结

一、自动配置介绍
我们都知道,在没有SpringBoot之前,利用Spring进行开发的时候,研发需要花费大量精力去定义模板化的各类配置文件。Spring最初使用Bean Factory以及动态代理实现各模块之间的解耦,它通过配置文件将bean扫描到Spring容器中。而SpringBoot将这种xml解析配置的过程,通过注解自动配置的方式来进行替换,它根据定义在classpath下的类,自动生成对应的bean,同时将其加载到Springcontext中。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);
		
	}


}

以上代码中,@SpringBootApplicationSpringBoot的核心注解,它是一系列注解的集合。它对应的源码如下所示。在这些注解当中@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自动配置过程用流程图进行表示,更能形象化的理解自动配置的流程。

这里写图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

慕枫技术笔记

您的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值