Bootique简介

2025/04/07

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的最新版本可在中央仓库中找到。

为了构建可运行的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
      cn.tuyucheng.taketoday.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将处理剩下的事情:

cn.tuyucheng.taketoday.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, tuyucheng!";
    }
 
    @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=/home/tuyucheng/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() );
}

9. 总结

在本文中,我们展示了如何使用Bootique的核心模块构建应用程序。还有其他几个可用的Bootique模块,如bootique-jooqbootique-kotlinbootique-job等,可用模块的完整列表可在此处查看。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章