SpringBoot自动装配原理、条件注解及封装Starter

1.什么是 SpringBoot 自动装配?

我们现在提到自动装配的时候,一般会和Spring Boot联系在一起。但是实际上SpringFramework 早就实现了这个功能。Spring Boot 只是在其基础上,通过 SPI 的方式,做了进一步优化。
SpringBoot 定义了一套接口规范:
SpringBoot 在启动时会扫描外部引用 jar 包中的META-INF/spring.factories文件,将文件中配置的类型信息加载到 Spring 容器(此处涉及到 JVM 类加载机制与 Spring 的容器知识),并执行类中定义的各种操作。对于外部 jar 来说,只需要按照 SpringBoot 定义的标准,就能将自己的功能装置进 SpringBoot。
没有 Spring Boot 的情况下,如果我们需要引入第三方依赖,需要手动配置,非常麻烦。但是,Spring Boot 中,我们直接引入一个 starter 即可。比如你想要在项目中使用 redis 的话,直接在项目中引入对应的 starter 即可。

<dependency>     
    <groupId>org.springframework.boot</groupId>    
    <artifactId>spring-boot-starter-data-redis</artifactId> 
</dependency> 

引入 starter 之后,我们通过少量注解和一些简单的配置就能使用第三方组件提供的功能了。

2.SpringBoot 是如何实现自动装配的?

依赖SpringBoot的核心注解实现 @SpringBootApplication

@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 {
    xxxx
}

其中@EnableAutoConfiguration 是实现自动装配的重要注解,我们以这个注解入手。

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

可以看到自动装配核心功能的实现实际是通过 AutoConfigurationImportSelector
那我们继续分析下AutoConfigurationImportSelector 类到底做了什么?
在这里插入图片描述

AutoConfigurationImportSelector:加载自动装配类

可以看到他是实现了 ImportSelector接口,也就实现了这个接口中的 selectImports方法,该方法主要用于获取所有符合条件的类的全限定类名,这些类需要被加载到 IoC 容器中。

public String[] selectImports(AnnotationMetadata annotationMetadata) {
        // 判断自动装配开关是否打开 默认是开启
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        // 获取获取所有需要装配的bean
        AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }

我们在继续看getAutoConfigurationEntry 方法是如何获取装配bean的

/**
* Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata}
* of the importing {@link Configuration @Configuration} class.
* @param annotationMetadata the annotation metadata of the configuration class
* @return the auto-configurations that should be imported
*/
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
    return EMPTY_ENTRY;
}
// 获取EnableAutoConfiguration注解中的 exclude 和 excludeName。
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 获取装配Bean
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 过滤重复bean
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
// 移除需要排除的bean
configurations.removeAll(exclusions);
// 根据AutoConfigurationImportFilter的match方法来判断是否符合OnBeanCondition,OnClassCondition,OnWebApplicationCondition 过滤bean
configurations = getConfigurationClassFilter().filter(configurations);
// 触发自动配置导入事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}

其中getCandidateConfigurationsgetConfigurationClassFilter().filter 方法为核心
getCandidateConfigurations 获取配置bean ,如图所示
在这里插入图片描述
主要调用 org.springframework.core.io.support.SpringFactoriesLoader#loadFactoryNames
再调用 org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories如图所示
其原理就是读取META-INF/spring.factories文件的的URL
在这里插入图片描述
getConfigurationClassFilter().filter 方法用于根据AutoConfigurationImportFilter的match方法来判断是否符合OnBeanCondition,OnClassCondition,OnWebApplicationCondition 过滤bean 如图所示
在这里插入图片描述
最后,总结下SpringBoot自动配置的原理,主要做了以下事情:

  1. spring.factories配置文件中加载自动配置类,利用org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories 方法实现
  2. 加载的自动配置类中排除掉@EnableAutoConfiguration注解的exclude属性指定的自动配置类;
  3. 再用AutoConfigurationImportFilter接口去过滤自动配置类是否符合其标注注解
    OnBeanCondition(如:ConditionalOnBean、ConditionalOnMissingBean)、OnClassCondition(如:ConditionalOnClass、ConditionalOnMissingClass)、OnWebApplicationCondition(如ConditionalOnWebApplication、ConditionalOnNotWebApplication)的条件,若都符合的话则返回匹配结果;
  4. 触发AutoConfigurationImportEvent事件
  5. 最后spring再将最后筛选后的自动配置类导入IOC容器中

拓展
1.条件注解
@ConditionalOnBean:当容器里有指定 Bean 的条件下
@ConditionalOnMissingBean:当容器里没有指定 Bean 的情况下
@ConditionalOnSingleCandidate:当指定 Bean 在容器中只有一个,或者虽然有多个但是指定首选 Bean
@ConditionalOnClass:当类路径下有指定类的条件下
@ConditionalOnMissingClass:当类路径下没有指定类的条件下
@ConditionalOnProperty:指定的属性是否有指定的值
@ConditionalOnResource:类路径是否有指定的值
@ConditionalOnExpression:基于 SpEL 表达式作为判断条件
@ConditionalOnJava:基于 Java 版本作为判断条件
@ConditionalOnJndi:在 JNDI 存在的条件下差在指定的位置
@ConditionalOnNotWebApplication:当前项目不是 Web 项目的条件下
@ConditionalOnWebApplication:当前项目是 Web 项 目的条件下
2.控制自动装配顺序注解
@AutoConfigureOrder
@AutoConfigureBefore
@AutoConfigureAfter
@AutoConfiguration(after = xx.class, before = xx.class)
@AutoConfigureOrder 此需要制定自定配置的顺序时,可以用 @AutoConfigureOrder ,表示绝对顺序(数字越小,优先顺序越高)。
@AutoConfigureBefore@AutoConfigureAfter 控制应用配置类的相对顺序。
具体顺序如下:

  1. 根据类名按照字母表递增排序
  2. 根据 @AutoConfigureOrder value 值(默认:0)递增排序
  3. 根据 @AutoConfigureBefore@AutoConfigureAfter 调整排序
    AutoConfigurationSorter 类是具体实现排序的逻辑

3.Starter编写

在这里插入图片描述在这里插入图片描述
是不是看着很简单,没错编写使用就是这么简单,但是若想控制注册顺序、注册条件依赖等需要了解清楚上述的条件注解和@Configuration+@Bean使用,自己尝试一下吧,封装一下自己常用的通用功能,简化一下代码;

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值