暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Spring Factories扩展机制

IT路人乙 2020-04-15
984

这里为了方便,我选择使用Spring Boot集成MyBatis,在MyBatis的实际使用中跟踪MyBatis的执行流程。下面我首先探索Spring Boot与MyBatis的集成原理。熟悉MyBatis的少年应该都知道使用步骤为:

  1. 根据MyBatis配置文件获取SqlSessionFactory实例;

  2. 使用SqlSessionFactory实例打开Session连接,从而获取SqlSession对象;

  3. 使用SqlSessionFactory实例打开Session连接,从而获取SqlSession对象;

  4. 适时关闭Session连接。

从上述使用步骤可清晰地抽取出:MyBatis对外提供的接口为SqlSessionFacory,其核心为SqlSession。那么,MyBatis是怎样实现Spring Boot扩展点的呢?这都依赖于Spring Factories扩展机制。

1 Spring Factories扩展机制

曾经青葱的小小少年,你肯定不知道JRE中给我们提供了一种非常便利的扩展机制:SPI扩展点。

Spring Factories扩展机制也就是建立在SPI扩展点的理论依据之上的。学会了它你又可以干一番泣鬼神的事业了。

SPI扩展点

SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的接口。SPI是调用方来制定接口规范,提供给外部实现,调用时选择自己需要的外部实现。实现步骤:

  • 定义一个接口

  • 在resources目录下新建META-INF/services目录,并在该目录下新建一个与接口全称一致的文件,并在该文件中写入接口实现的全称

  • 通过ServiceLoader加载实现类并调用

机制中核心是ServiceLoader类的加载过程。

SpringBoot中的SPI扩展机制

在Spring中也有一种类SPI的加载机制。它在META-INF/spring.factories文件中配置接口的实现类全称。然后在程序中读取这些配置文件并实例化实现。详细使用步骤如下:

  • 实现插入点适配器

  • 条件配置插入点适配器

  • 在META-INF/spring.factories文件中以EnableAutoConfiguration为key申明条件配置

  • 在starter中暴露适配接口

通常,对于SpringBoot的扩展而言,你应该遵循一定的命名规则。假如,你的扩展点为icoffee
项目(这个名字灵感来源于这几天被爆头的luckin coffee),那么插入点适配器工程名应该为icoffee-spring
;条件配置和spring.factories文件位于同一个工程中,工程名应该为icoffee-spring-boot-autoconfigure
;starter壳的名字应该为icoffee-spring-boot-starter

Spring Factories实现原理分析

实际上,Spring Factories扩展级机制的幕后英雄是spring-core包中给我们提供的两个工具类:SpringFactoriesLoader类和PropertiesLoaderUtils抽象类。这部分我们就走进它们的世界,探索其设计意图。SpringFactoriesLoader类的代码实现很简单:首先申明factories文件名称;然后设置私有无参构造器,使其不能被new;最后暴露两个静态方法供外部使用(loadFactories方法和loadFactoryNames方法)。而在loadFactoryNames方法中又依赖PropertiesLoaderUtils类解析factories文件。现在, 我们成功地从factories文件中获取了自动配置类的全称。接下来,我们应该怎样将这些自动配置类导入到Spring中呢?这得益于@Import注解。@Import注解用来导入@Configuration注解的配置类、声明@Bean注解的bean方法、导入ImportSelector实现类或导入ImportBeanDefinitionRegistrar的实现类。基于这样的理论依据,我们打开@EnableAutoConfiguration的类文件,

进入AutoConfigurationImportSelector的源码,你不难发现selectImports方法委托getCandidateConfigurations方法来获取配置类全称;而后者又将解析/META-INF/spring.factories的累活委托给了SpringFactoriesLoader类实现,代码片段如下

就这样你自定义的配置类就被导入到Spring中了。接下来,我将基于Spring Factories扩展机制将icoffee
项目加载到Spring上下文中。

2 自定义Starter

这里我假设开发一个icoffee
项目,项目中包含MathLib接口
,接口中包含一个calc1()
方法,该方法主要用来计算一个数只能由1的倍数或3的倍数组成(n=1*x+3*y
)的解的数量。
项目使用方法为

@Test
public void testICoffee() {
MathLibFactoryBuilder builder = new MathLibFactoryBuilder();
// 获取工厂对象
MathLibFactory mathLibFactory = builder.builder();
// 使用工厂对象获取MathLib实例
MathLib mathLib = mathLibFactory.getMathLib();
// 使用MathLib的calc1方法求解
int c = mathLib.calc1(5);
Assert.assertEquals(2, c);
}

使用方法是不是很熟悉?确实,这就是模仿MyBatis框架的使用方法开发的。项目结果如下图

接下来,我会基于Spring Factories机制开发icoffee项目的Spring Boot Starter。Starter项目规划如下

icoffee-spring
适配器工程中主要包装一个MathLibFactory
类的FactoryBean以供Spring框架使用。

icoffee-spring-boot-autoconfigure
工程主要负责自动配置FactoryBean和在spring.factories文件中申明自动配置类。

icoffee-spring-boot-starter
工程没有任何源码,主要负责icoffee和相关扩展的依赖申明。

icoffee-example
工程通过icoffee starter使用icoffee项目。使用方法很简单,只需要在pom文件中引入start即可,如下

<dependency>
<groupId>org.johnny.icoffee</groupId>
<artifactId>icoffee-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>

引入starter依赖之后,即可将icoffee及相关依赖全部引入到example工程中。接下来就可以像使用正常的Spring Bean一样使用MathLibFactory了。我这里写了一个web接口,请求路径为/icoffee/{n}
代码片段如下

@RestController
public class ICoffeeController {

// 注入MathLibFactory实例
private @Autowired MathLibFactory mathLibFactory;

@GetMapping("/icoffee/{n}")
public String testCalc1(@PathVariable Integer n) {
// 调用MathLib的calc1()方法计算
int i = this.mathLibFactory.getMathLib().calc1(n);
System.out.println("i -> "+i);
return String.valueOf(i);
}
}

启动example工程就可以发送http get请求到http://localhost:8080/icoffee/5路径验证结果。想要源码就点下面的按钮打赏下我(^▽^),开玩笑的!源码你可以在github上找到。

最近在研究瑞幸咖啡的发展过程及被狙的原因,感觉瑞幸咖啡的宗旨应该定位为让咖啡就像沙县小吃一样走进千家万户走进大千世界。咖啡事件真的能触动很多人,人与人的区别啊,没有别的,真的只有思维方式。踏入社会,大学生涯才真正开始!

文章转载自IT路人乙,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论