源码级深度理解 Java SPI( 五 )

从 getFactory 方法的源码可以看出 , 其核心逻辑分为 4 步:

  • 首先,尝试查找全局属性org.apache.commons.logging.LogFactory,如果指定了具体类,尝试创建实例 。
  • 利用 Java SPI 机制,尝试在 classpatch 的 META-INF/services 目录下寻找org.apache.commons.logging.LogFactory 的实现类 。
  • 尝试从 classpath 目录下的 commons-logging.properties 文件中查找org.apache.commons.logging.LogFactory 属性,如果指定了具体类,尝试创建实例 。
  • 以上情况如果都不满足 , 则实例化默认实现类 , 即org.apache.commons.logging.impl.LogFactoryImpl 。
4.3 SPI 应用案例之 Spring BootSpring Boot 是基于 Spring 构建的框架,其设计目的在于简化 Spring 应用的配置、运行 。在 Spring Boot 中,大量运用了自动装配来尽可能减少配置 。
下面是一个 Spring Boot 入口示例,可以看到,代码非常简洁 。
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;@SpringBootApplication@RestControllerpublic class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}@GetMapping("/hello")public String hello(@RequestParam(value = "https://www.huyubaike.com/biancheng/name", defaultValue = "https://www.huyubaike.com/biancheng/World") String name) {return String.format("Hello %s!", name);}}那么,Spring Boot 是如何做到寥寥几行代码 , 就可以运行一个 Spring Boot 应用的呢 。我们不妨带着疑问 , 从源码入手,一步步探究其原理 。
4.3.1 @SpringBootApplication 注解首先,Spring Boot 应用的启动类上都会标记一个
@SpringBootApplication 注解 。
@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 {// 略}除了 @Target、 @Retention、@Documented、@Inherited 这几个元注解, @SpringBootApplication 注解的定义中还标记了 @SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan 三个注解 。
4.3.2 @SpringBootConfiguration 注解从@SpringBootConfiguration 注解的定义来看,@SpringBootConfiguration 注解本质上就是一个 @Configuration 注解,这意味着被@SpringBootConfiguration 注解修饰的类会被 Spring Boot 识别为一个配置类 。
@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Configurationpublic @interface SpringBootConfiguration {@AliasFor(annotation = Configuration.class)boolean proxyBeanMethods() default true;}4.3.3 @EnableAutoConfiguration 注解@EnableAutoConfiguration 注解定义如下:
@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage@Import({AutoConfigurationImportSelector.class})public @interface EnableAutoConfiguration {String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";Class<?>[] exclude() default {};String[] excludeName() default {};}@EnableAutoConfiguration 注解包含了 @AutoConfigurationPackage与 @Import({AutoConfigurationImportSelector.class}) 两个注解 。
4.3.4 @AutoConfigurationPackage 注解@AutoConfigurationPackage 会将被修饰的类作为主配置类,该类所在的 package 会被视为根路径,Spring Boot 默认会自动扫描根路径下的所有 Spring Bean(被 @Component 以及继承 @Component 的各个注解所修饰的类) 。——这就是为什么 Spring Boot 的启动类一般要置于根路径的原因 。这个功能等同于在 Spring xml 配置中通过 context:component-scan 来指定扫描路径 。@Import 注解的作用是向 Spring 容器中直接注入指定组件 。@AutoConfigurationPackage 注解中注明了@Import({Registrar.class}) 。Registrar 类用于保存 Spring Boot 的入口类、根路径等信息 。
4.3.5 SpringFactoriesLoader.loadFactoryNames 方法@Import(AutoConfigurationImportSelector.class) 表示直接注入AutoConfigurationImportSelector 。
AutoConfigurationImportSelector 有一个核心方法getCandidateConfigurations 用于获取候选配置 。该方法调用了SpringFactoriesLoader.loadFactoryNames 方法 , 这个方法即为 Spring Boot SPI 的关键 , 它负责加载所有 META-INF/spring.factories 文件,加载的过程由 SpringFactoriesLoader 负责 。
Spring Boot 的 META-INF/spring.factories 文件本质上就是一个 properties 文件,数据内容就是一个个键值对 。

推荐阅读