SpringBoot自动配置注解原理解析
Posted 逐客教我
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot自动配置注解原理解析相关的知识,希望对你有一定的参考价值。
1. SpringBoot启动主程序类:
1 @SpringBootApplication 2 public class DemoApplication { 3 public static void main(String[] args) { 4 5 SpringApplication.run(DemoApplication.class, args); 6 } 7 }
每次我们直接直接启动这个启动类,SpringBoot就启动成功了,并且帮我们配置了好多自动配置类。
其中最重要是 @SpringBootApplication 这个注解,我们点进去看一下。
2. SpringBootApplication注解:
1 @Target(ElementType.TYPE) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Documented 4 @Inherited 5 @SpringBootConfiguration 6 @EnableAutoConfiguration 7 @ComponentScan(excludeFilters = { 8 @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), 9 @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) 10 public @interface SpringBootApplication {
三个比较重要的注解:
-
@SpringBootConfiguration : Spring Boot的配置类,标注在某个类上,表示这是一个Spring Boot的配置类
-
@EnableAutoConfiguration: 开启自动配置类,SpringBoot的精华所在。
-
@ComponentScan包扫描
以前我们需要配置的东西,Spring Boot帮我们自动配置;@EnableAutoConfiguration告诉SpringBoot开启自动配置功能;这样自动配置才能生效;
3. EnableAutoConfiguration注解:
1 @Target(ElementType.TYPE) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Documented 4 @Inherited 5 @AutoConfigurationPackage 6 @Import(AutoConfigurationImportSelector.class) 7 public @interface EnableAutoConfiguration {
两个比较重要的注解:
-
@AutoConfigurationPackage:自动配置包
-
@Import: 导入自动配置的组件
4. AutoConfigurationPackage注解:
1 static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { 2 3 @Override 4 public void registerBeanDefinitions(AnnotationMetadata metadata, 5 BeanDefinitionRegistry registry) { 6 register(registry, new PackageImport(metadata).getPackageName()); 7 }
它其实是注册了一个Bean的定义。
new PackageImport(metadata).getPackageName(),它其实返回了当前主程序类的 同级以及子级 的包组件。
以上图为例,DemoApplication是和demo包同级,但是demo2这个类是DemoApplication的父级,和example包同级
也就是说,DemoApplication启动加载的Bean中,并不会加载demo2,这也就是为什么,我们要把DemoApplication放在项目的最高级中。
5. Import(AutoConfigurationImportSelector.class)注解:
可以从图中看出 AutoConfigurationImportSelector 继承了 DeferredImportSelector 继承了 ImportSelector
ImportSelector有一个方法为:selectImports。
1 @Override 2 public String[] selectImports(AnnotationMetadata annotationMetadata) { 3 if (!isEnabled(annotationMetadata)) { 4 return NO_IMPORTS; 5 } 6 AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader 7 .loadMetadata(this.beanClassLoader); 8 AnnotationAttributes attributes = getAttributes(annotationMetadata); 9 List<String> configurations = getCandidateConfigurations(annotationMetadata, 10 attributes); 11 configurations = removeDuplicates(configurations); 12 Set<String> exclusions = getExclusions(annotationMetadata, attributes); 13 checkExcludedClasses(configurations, exclusions); 14 configurations.removeAll(exclusions); 15 configurations = filter(configurations, autoConfigurationMetadata); 16 fireAutoConfigurationImportEvents(configurations, exclusions); 17 return StringUtils.toStringArray(configurations); 18 }
可以看到第九行,它其实是去加载 public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";外部文件。这个外部文件,有很多自动配置的类。如下:
6. 如何自定义自己的Bean:
我们以RedisTemplate为例:
1 @Configuration 2 @ConditionalOnClass(RedisOperations.class) 3 @EnableConfigurationProperties(RedisProperties.class) 4 @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) 5 public class RedisAutoConfiguration { 6 7 @Bean 8 @ConditionalOnMissingBean(name = "redisTemplate") 9 public RedisTemplate<Object, Object> redisTemplate( 10 RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { 11 RedisTemplate<Object, Object> template = new RedisTemplate<>(); 12 template.setConnectionFactory(redisConnectionFactory); 13 return template; 14 } 15 16 @Bean 17 @ConditionalOnMissingBean 18 public StringRedisTemplate stringRedisTemplate( 19 RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { 20 StringRedisTemplate template = new StringRedisTemplate(); 21 template.setConnectionFactory(redisConnectionFactory); 22 return template; 23 } 24 25 }
我们每次在Spring中使用Redis,都会使用到RedisTemplate这个工具类,但是他默认给我们返回的这个工具类,可能不是很符合我们的要求。比如:我们想要开启事务,或者想要改变它默认的序列化。
这时候该如何去做呢?
根据前面的分析,只要我们在容器中放入一个RedisTemplate Bean即可。
1 @Bean("redisTemplate") 2 public RedisTemplate<Object, Object> myRedisTemplate( 3 RedisConnectionFactory redisConnectionFactory) { 4 RedisTemplate<Object, Object> template = new RedisTemplate<>(); 5 template.setConnectionFactory(redisConnectionFactory); 6 // 修改序列化为Jackson 7 template.setDefaultSerializer(new GenericJackson2JsonRedisSerializer()); 8 // 开启事务 9 template.setEnableTransactionSupport(true); 10 return template; 11 }
我们自己定义我们的RedisTemplate模板,修改序列化,开启事务等操作。
我们将我们自己的Bean加入到IoC容器中以后,他就会默认的覆盖掉原来的RedisTemplate,达到定制的效果。
我们在以Kafka为例:
假设我们想要消费的对象不是字符串,而是一个对象呢?比如Person对象,或者其他Object类呢?
1:我们首先去查找KafkaAutoConfiguration(xxxAutoConfiguration),看看是否有关于Serializer属性的配置
2:假设没有我们就去KafkaProperties文件查找是否有Serializer的配置
然后直接在application.properties修改默认序列化就好,连Bean都不需要自己重写。
类似这种,可以使用Spring提供的Json序列化,也可以自动使用第三方框架提供的序列化,比如Avro, Protobuff等
1 spring.kafka.producer.key-serializer=org.springframework.kafka.support.serializer.JsonSerializer 2 spring.kafka.producer.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer 3 spring.kafka.consumer.key-deserializer=com.example.common.MyJson 4 spring.kafka.consumer.value-deserializer=com.example.common.MyJson
后记:
- 很多时候我们刚开始看一个知识点,不懂迷茫的时候,真的不要慌,可能说明你暂时的知识储备还不够理解。
- 等你经过不断的学习,不断的深入以后
- 突然有一天,你会突然的醒悟
- 哇!原来他讲的是这个意思
- 可能我们不理解,我们可以去尝试去看两遍,三遍,甚至更多遍,突然会有一个时刻,你会醒悟过来的
- 且行且珍惜,共勉
以上是关于SpringBoot自动配置注解原理解析的主要内容,如果未能解决你的问题,请参考以下文章