Summer - 基于Java9的模块化框架

为什么叫Summer

因为夏天到了 :D

项目地址

特点

  • 使用了JPMS作为模块化的基础
  • 可以通过配置文件动态的选择启动哪些模块
  • 使用SPI的方式进行创建对象,并能自动注入所需要的服务(也可以选择手动)
  • 自动注入模块配置文件

项目结构

项目主要分为三个模块

  • 核心模块: 对模块和提供者进行定义,发现加载模块和注入
  • 启动模块: 引导模块
  • 工具模块: 基础工具

使用方式

模块

首先需要定义模块类,例如仓库中的例子A模块,默认情况下,你只要定义类就行.
除了模块定义之外还需要定义该模块应能提供的服务

1
2
3
4
5
6
public interface ModuleAService extends Service {

void startAService();

Integer doSomeWork();
}

以下是ModuleA的module-info.java
1
2
3
4
5
6
7
8
module module.a {
requires summer.modular;

exports io.whileaway.code.summer.example.module.a.services;
exports io.whileaway.code.summer.example.module.a;
provides ModuleDefine with ModuleA; // 需要进行指定实现了ModuleDefine 便于SPI使用

}

提供者

我们先来看A模块的第一个提供者A1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Provide(ModuleA.class) // 需要指定是哪个模块的提供者
public class A1Provider extends ModuleProvider {

ModuleConfig config = new ProviderConfigA();

public A1Provider() {
// 传入lookup是为了service的自动注入
// 在Java模块系统中由于模块之间的限制,可以使用这种方式进行传递
// 虽然可以使用open 但那样的话感觉给的权限就过大了
super(MethodHandles.lookup());
}

@Override
public ModuleConfig providerConfig() {
return config; // 返回持有的config对象用于框架自动注入配置
}
}

如果不需要config可以不创建config对象,创建的config对象需要继承ModuleConfig类
创建配置类中的成员变量可以仅指定getter方法
配置类是从application.yml中进行读取配置
1
2
3
4
5
6
module.a:
selector: ${A_S:provider.a1} # 当前选择使用a1
provider.a1: # 针对提供者a1的配置 以下的配置都会被自动填入config中
name: providerA1
provider.a2: # 针对提供者a2的配置
name: providerA2

接下来创建模块定义接口的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 继承于模块中的接口
public class ModuleAServiceImpl implements ModuleAService {

// 成员变量config会自动进行注入 后面会有注入其他模块中类的方法
private ProviderConfigA config;

@Override
public void startAService() {
System.out.println("module A config name: " + config.getName());
}

@Override
public Integer doSomeWork() {
return 42;
}
}

以下是a1提供者module-info.java
1
2
3
4
5
6
7
8
9
10
11
12
13
import io.whileaway.code.summer.example.provider.a1.A1Provider;
import io.whileaway.code.summer.example.provider.a1.ModuleAServiceImpl;
import io.whileaway.code.summer.modular.ModuleProvider;
import io.whileaway.code.summer.modular.Service;

module provider.a1 {
requires summer.modular;
requires module.a;
requires lombok;

provides Service with ModuleAServiceImpl; // 表明模块中需要被发现的服务
provides ModuleProvider with A1Provider; // 标识模块中的提供者类
}

当提供者之间出现依赖的情况

在样例代码中有提供者B

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Provide(ModuleB.class)
@Require(moduleName = {ModuleA.class}) // 所要求的模块列表
public class BProvider extends ModuleProvider {

ModuleConfig config = new ProviderConfigB();

public BProvider() {
super(MethodHandles.lookup());
}

@Override
public ModuleConfig providerConfig() {
return config;
}

@Override
public void start() throws ServiceException { // 模块启动后所要执行的方法
getService(ModuleBService.class).startBService();
}
}

这时B的服务中若有定义A的服务并标注注入注解就能自动的注入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ModuleBServiceImpl implements ModuleBService {

private ProviderConfigB config;

@Inject
private ModuleAService aService;

@Override
public void startBService() {
aService.startAService();
System.out.println("B config: " + config.getName());
System.out.println("in B Service get A service number: " + aService.doSomeWork());
}
}

Author: Sean
Link: https://blog.whileaway.io/posts/97d551a7/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.