spring boot实战自动配置原理分析以及SpringBoot starter的理解

@Import({AutoConfigurationImportSelector.class})通过获取指定文件路径,导入路径下所有的bean到系统。
@Conditional 条件判断注解,
 @ConditionalOnClass({RedisOperations.class}) 判断RedisOperations.clss 在系统中是否存在,不存在跳过
 @ConditionalOnMissingBean({RedisConnectionFactory.class}) 表示的意思为:如果存在名称为RedisConnectionFactory的bean,则该部分代码直接忽略,这是Spring Boot人性化体现之一,开发者申明的bean会放在第一位,实在是太懂礼貌了~
@EnableConfigurationProperties({RedisProperties.class}) 引入配置类注解

springboot有哪些特别:

  1. 快速搭建项目的脚手架。

  2.依赖管理特性 更多的maven 的特性,但是springboot 给做到了统一做成了工具类。

    2.1 提供了统一的版本控制,可以有效解决版本冲突。在父项目(spring-boot-starter-parent)通过 <dependencyManagement> 管理各种版本 和依赖

    2.2提供了很多 spring-boot-start(每一个start就相当于一种场景) ,解决了对应场景所依赖的jia 包

  3.自动配置特性。

通过 @SpringBootApplication 注解导入

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

通过 @EnableAutoConfiguration 注解

复制代码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
复制代码

1. 分析 @AutoConfigurationPackage   总结 :它主要是获取容器的最外层包名利用@import 注解讲该包下的所有组件(bean)全部导入到容器中,完成我开发自定义bean的加载

 

 

1
2
3
4
5
6
7
8
9
10
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({AutoConfigurationPackages.Registrar.class}) //<strong>它主要是获取容器的最外层包名利用@import 注解讲该包下的所有组件(bean)全部导入到容器中</strong>
public @interface AutoConfigurationPackage {
    String[] basePackages() default {};
 
    Class<?>[] basePackageClasses() default {};
}

 

  

 

 

 

 

2. 分析  @Import({AutoConfigurationImportSelector.class})

总结 :

1.他会先加载所有的自动配置类 xxxxAutoConfiguration

2. 每个配置类按照条件进行生效,默认都会绑定配置文件指定的值。xxxxProperties里面拿,xxxxProperties类和配置文件进行绑定。

3.生效的配置类,就会给容器中配置很多组件。有了组件 功能就有了。

 

org.springframework.boot.autoconfigure.AutoConfigurationImportSelector  

 

 

 

 

spring 加载自动配置类依靠的是 SpringFactoriesLoader 类,该类会自动加载 ClassLoader 下所有 jar 包中的 META-INF/spring.factories 文件所配置的 Bean。

舉例子: RedisAutoConfiguration

配置类

首先来看RedisAutoConfiguration类上的注解:

@AutoConfiguration
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {


@ConditionalOnClass:表示存在对应的Class文件时才会去解析RedisAutoConfiguration,否则直接跳过不解析,这也是为什么在不导入Redis依赖Jar时工程能正常启动的原因

@EnableConfigurationProperties:表示对@ConfigurationProperties的内嵌支持,默认会将对应Class这是为bean,例如这里值为RedisProperties.class,其定义为:

复制代码
@ConfigurationProperties(
prefix = "spring.redis"
)
public class RedisProperties {
private int database = 0;
private String url;
private String host = "localhost";
private String username;
private String password;
private int port = 6379;
private boolean ssl;
private Duration timeout;
private Duration connectTimeout;
private String clientName;
private ClientType clientType;
private Sentinel sentinel;
private Cluster cluster;
private final Jedis jedis = new Jedis();
private final Lettuce lettuce = new Lettuce();
复制代码

 


RedisProperties提供对redis的配置信息,其前缀为spring.redis

,因此在上篇中配置的host、port等信息会配置到该类上,

随后@ EnableConfigurationProperties会将RedisProperties注册为一个bean。

@Import为导入配置,LettuceConnectionConfiguration

具体实现如下:

复制代码


@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({RedisClient.class})
@ConditionalOnProperty(
name = {"spring.redis.client-type"},
havingValue = "lettuce",
matchIfMissing = true
)
class LettuceConnectionConfiguration extends RedisConnectionConfiguration {
LettuceConnectionConfiguration(RedisProperties properties, ObjectProvider<RedisStandaloneConfiguration> standaloneConfigurationProvider, ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider, ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider) {
super(properties, standaloneConfigurationProvider, sentinelConfigurationProvider, clusterConfigurationProvider);
}

@Bean(
destroyMethod = "shutdown"
)
@ConditionalOnMissingBean({ClientResources.class})
DefaultClientResources lettuceClientResources(ObjectProvider<ClientResourcesBuilderCustomizer> customizers) {
DefaultClientResources.Builder builder = DefaultClientResources.builder();
customizers.orderedStream().forEach((customizer) -> {
customizer.customize(builder);
});
return builder.build();
}

@Bean
@ConditionalOnMissingBean({RedisConnectionFactory.class})
LettuceConnectionFactory redisConnectionFactory(ObjectProvider<LettuceClientConfigurationBuilderCustomizer> builderCustomizers, ClientResources clientResources) {
LettuceClientConfiguration clientConfig = this.getLettuceClientConfiguration(builderCustomizers, clientResources, this.getProperties().getLettuce().getPool());
return this.createLettuceConnectionFactory(clientConfig);
}
 
复制代码

 

这里又涉及到一个重要的注解:@ConditionalOnMissingBean,其功能为如果存在指定name的bean,则该注解标注的bean不创建, @ConditionalOnMissingBean({RedisConnectionFactory.class})


表示的意思为:如果存在名称为RedisConnectionFactory的bean,则该部分代码直接忽略,这是Spring Boot人性化体现之一,开发者申明的bean会放在第一位,实在是太懂礼貌了~~

本篇中涉及到的注解比较多,其具体实现原理在以后有时间再具体分析。

再回到RabbitAutoConfiguration类的具体实现
首先来看:

复制代码


@AutoConfiguration
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
public RedisAutoConfiguration() {
}

@Bean
@ConditionalOnMissingBean(
name = {"redisTemplate"}
)
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}

@Bean
@ConditionalOnMissingBean
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
return new StringRedisTemplate(redisConnectionFactory);
}
}

复制代码

 

 

 

@EnableAutoConfiguration  自动装配原理
  1. 分析 @AutoConfigurationPackage  它主要是获取容器的最外层包名利用@import 注解讲该包下的所有组件(bean)全部导入到容器中,完成我开发自定义bean的加载
  2.@Import({AutoConfigurationImportSelector.class})自动配置文件收集器

    1.他会先通过  SpringFactoriesLoader 类的 FactoryClassNames 读取所有的自动配置类 xxxxAutoConfiguration 的全限定类名

    2. 每个配置类按照条件进行生效(通过条件类@ConditionalOnClass({RedisOperations.class} 判断我们系统中是否有对应的字节码文件(我们引入了对应的jar包后字节码就有了),有的话就加载配置类,没有的就跳过 ) )。

               3. 每个自动配置的类都有 (@EnableConfigurationProperties({RedisProperties.class})) 默认都会绑定配置文件指定的值。xxxxProperties里面拿,xxxxProperties类和配置文件进行绑定。系统中会给出默认的配置,我们也可以在配置文件中,覆盖系统给的默认配置。

    4. 生效的配置类,就会被容器创建。有了组件功能就有了。


1
2
3
注意
1. 自动注入 只注入   META-INF/spring.factories 中 key = org.springframework.boot.autoconfigure.EnableAutoConfiguration
2. 我们自己写主动注入的时候必须要在 写 META-INF/spring.factories 中 key = org.springframework.boot.autoconfigure.EnableAutoConfiguration
依赖管理特性:
版本控制 jia包依赖 管理:
  springBoot 做了很多的start包, 通过提前在start中定义好版本,和管理好对应的jar包,子项目在使用的时候,就可以不用指定版本了和jar包依赖了。
SpringBoot starter的理解?

  starter 可以理解为一种使用场景。这个场景需要用到哪些组件,当前start 会统一管理好,引入好。,Spring框架也给我们实现了将近50个好用的starter。

比如以前我们要用Mybatis框架,可能会引入各种的包才能使用。而starter就是做了一层封装,把相关要用到的jar都给包起来了,并且也写好了对应的版本。这我们使用的时候就不需要引入一堆jar包且管理版本类似的问题了。

 


 现在很多开源的组件都会提供对应的springboot-starter包给我们去用,要做一个starter包并不难。参照Spring内置的实现就好了:1、在工程里引入 starter 打包相关的依赖。2、在我们工程内建spring.factories文件,编写我们配置类的全限类名。


.springboot的自动配置原理总结?

1.通过@SpringBootConfiguration 引入了@EnableAutoConfiguration (负责启动自动配置功能)

2.@EnableAutoConfiguration 引入了@Import

3.Spring容器启动时:加载Ioc容器时会解析@Import 注解

4.@Import导入了一个deferredImportSelector(它会使SpringBoot的自动配置类的顺序在最后,这样方便我们扩展和覆盖?)

5.然后读取所有的/META-INF/spring.factories文件(SPI)

6.过滤出所有AutoConfigurtionClass类型的类

7.最后通过@ConditioOnXXX排除无效的自动配置类

项目实际使用:
  1.公司在引入rocketMq的时候,在放在一个公司的通用的rpc common中,在这里用start的方式提前注入了 生产者 消费者 。这样我们在项目在使用的时候,只需要引入我们common 中自定义的start就行,项目启动后satrt通过自动注入的方式 注入mq相关的Bean
,我们在使用的时候,直接从ioc容器中获取生产者消费者bean就可以使用了。我们不需引入mq原始额包啊,导入mq的各种配置,以及mq相关对象的实例化了。
  2. 和外部对接时。自定义了一个 加密认证的规则。做成了 start。这样使用起来方便
posted @   好记性不如烂笔头=>  阅读(384)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示