Bootique 简介
1. 概述
Bootique 是一个非常轻量级的开源无容器 JVM 框架,旨在构建下一代可扩展的微服务。它建立在嵌入式 Jetty 服务器之上,并完全支持带有jax-rs 的REST处理程序。
在本文中,我们将展示如何使用Bootique构建一个简单的 Web 应用程序。
2. Maven依赖
让我们通过在 pom.xml 中添加以下依赖项来开始使用Bootique:
<dependency>
<groupId>io.bootique.jersey</groupId>
<artifactId>bootique-jersey</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.bootique</groupId>
<artifactId>bootique-test</artifactId>
<scope>test</scope>
</dependency>
但是,Bootique还需要声明一些BOM导入。这就是为什么需要在pom.xml中添加以下dependencyManagement部分:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.bootique.bom</groupId>
<artifactId>bootique-bom</artifactId>
<version>0.23</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
最新版本的Bootique在Central Maven Repository 中可用。
为了构建一个可运行的 jar,Bootique依赖于maven-shade-plugin 。这就是为什么我们还需要添加以下配置:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
</plugin>
</plugins>
</build>
3. 启动应用程序
启动Bootique应用程序的最简单方法是从 main 方法调用*Bootique 的exec()*方法:
public class App {
public static void main(String[] args) {
Bootique.app(args)
.autoLoadModules()
.exec();
}
}
**但是,这不会启动嵌入式服务器。**运行上述代码后,应显示以下日志:
NAME
com.blogdemo.bootique.App
OPTIONS
-c yaml_location, --config=yaml_location
Specifies YAML config location, which can be a file path
or a URL.
-h, --help
Prints this message.
-H, --help-config
Prints information about application modules and their
configuration options.
-s, --server
Starts Jetty server.
这些只是Bootique预先捆绑的可用程序参数。
这些名称是不言自明的;因此,要启动服务器,我们需要传递*–s或–server*参数,服务器将启动并在默认端口 8080上运行。
4. 模块
Bootique应用程序由“模块”的集合组成。在Bootique的术语中,“模块是包含一些代码的 Java 库”,这意味着它将每个服务都视为一个模块。它使用*Google Guice * 进行依赖注入。
为了看看它是如何工作的,让我们创建一个接口:
public interface HelloService {
boolean save();
}
现在,我们需要创建一个实现:
public class HelloServiceImpl implements HelloService {
@Override
public boolean save() {
return true;
}
}
有两种方法可以加载模块。第一种是使用Guice的*Module 接口,另一种是使用Bootique*的BQModuleProvider 也称为自动加载。
4.1. Guice 模块
在这里,我们可以使用Guice的*Module *接口来绑定实例:
public class ModuleBinder implements Module {
@Override
public void configure(Binder binder) {
binder
.bind(HelloService.class)
.to(HelloServiceImpl.class);
}
}
定义模块后,我们需要将此自定义模块映射到Bootique实例:
Bootique
.app(args)
.module(module)
.module(ModuleBinder.class)
.autoLoadModules()
.exec();
4.2. BQModuleProvider(自动加载)
在这里,我们需要做的就是使用BQModuleProvider定义之前创建的模块绑定器:
public class ModuleProvider implements BQModuleProvider {
@Override
public Module module() {
return new ModuleBinder();
}
}
这种技术的优点是我们不需要将任何模块信息映射到Bootique实例。
我们只需要在*/resources/META-INF/services/io.bootique.BQModuleProvider中创建一个文件,并写入ModuleProvider的全名,包括包名,其余的由Bootique*处理:
com.blogdemo.bootique.module.ModuleProvider
现在,我们可以使用*@Inject *注解在运行时使用服务实例:
@Inject
HelloService helloService;
这里需要注意的一件重要事情是,由于我们使用的是Bootique自己的 DI 机制,因此我们不需要使用*Guice @ImplementedBy *注解来绑定服务实例。
5. REST端点
使用 JAX-RS API 创建 REST 端点很简单:
@Path("/")
public class IndexController {
@GET
public String index() {
return "Hello, blogdemo!";
}
@POST
public String save() {
return "Data Saved!";
}
}
要将端点映射到Bootique自己的Jersey实例,我们需要定义JerseyModule :
Module module = binder -> JerseyModule
.extend(binder)
.addResource(IndexController.class);
6. 配置
我们可以在基于 YAML 的属性文件中提供内置或自定义配置信息。 例如,如果我们想在自定义端口上启动应用程序并添加默认 URI 上下文“hello”,我们可以使用以下 YAML 配置:
jetty:
context: /hello
connector:
port: 10001
现在,在启动应用程序时,我们需要在 config 参数中提供此文件的位置:
--config=/home/blogdemo/bootique/config.yml
7. 日志
开箱即用的Bootique带有一个bootique-logback 模块。要使用此模块,我们需要在pom.xml中添加以下依赖项:
<dependency>
<groupId>io.bootique.logback</groupId>
<artifactId>bootique-logback</artifactId>
</dependency>
这个模块带有一个*BootLogger *接口,我们可以覆盖它来实现自定义日志记录:
Bootique.app(args)
.module(module)
.module(ModuleBinder.class)
.bootLogger( new BootLogger() {
@Override
public void trace( Supplier<String> args ) {
// ...
}
@Override
public void stdout( String args ) {
// ...
}
@Override
public void stderr( String args, Throwable thw ) {
// ...
}
@Override
public void stderr( String args ) {
// ...
}
}).autoLoadModules().exec();
此外,我们可以在config.yaml文件中定义日志记录配置信息:
log:
level: warn
appenders:
- type: file
logFormat: '%c{20}: %m%n'
file: /path/to/logging/dir/logger.log
8. 测试
对于测试,Bootique带有bootique-test 模块。有两种方法可以测试Bootique应用程序。
第一种方法是*“前台”*方法,它使所有测试用例在主测试线程上运行。
另一种是*“后台”*方法,它使测试用例在隔离的线程池上运行。
可以使用BQTestFactory 初始化“前台”环境:
@Rule
public BQTestFactory bqTestFactory = new BQTestFactory();
可以使用BQDaemonTestFactory 初始化“后台”环境:
@Rule
public BQDaemonTestFactory bqDaemonTestFactory = new BQDaemonTestFactory();
一旦环境工厂准备就绪,我们就可以编写简单的测试用例来测试服务:
@Test
public void givenService_expectBoolen() {
BQRuntime runtime = bqTestFactory
.app("--server").autoLoadModules()
.createRuntime();
HelloService service = runtime.getInstance( HelloService.class );
assertEquals( true, service.save() );
}