Spring Boot 与 Tio Boot 的整合

## Spring Boot 与 Tio Boot 的整合

### 概述

**什么是 Spring Boot 框架?**

- Spring Boot 是一个广泛使用的框架,它提供了一种快速、简单的方式来设置和启动 Spring 应用。它的自动配置、内置服务器和控制器使得开发 Web 应用变得更加容易。


**为什么使用 Spring Boot 框架整合 Tio-Boot?**

使用 Spring Boot 整合 Tio-Boot 有以下几个优势:

- Tio-Boot 框架的代码可以在 Spring Boot 框架下运行。

- 开发时使用 Tio-Boot 框架进行快速开发,开发完成后使用 Spring Boot 框架运行。

- 可以利用 Spring Boot 对微服务的支持,将 Tio-Boot 框架作为一个微服务整合到 Spring Cloud 中。


**Spring Boot 和 Tio-Boot 能否实现无缝整合?**

- 无法实现完全无缝的整合。

- 对于 HTTP 服务器、请求参数封装、控制器以及微服务,仍然使用 Spring Boot 框架。

- 对于其他服务,如服务器和数据库查询等,使用 Tio-Boot 框架。

- 整合 Spring Boot 之后因为使用的是 spring-boot 内置的 http server 将无法使用 tio boot 的异步和非阻塞的方式处理网络请求


### 添加依赖

请将以下依赖添加到你的项目中,这些依赖包括 spring-boot 和 tio-boot 的相关依赖,以及一些其他必要的库。

1. **properties**:这部分定义了一些全局变量,可以在 pom.xml 文件的其他地方引用。例如,`${java.version}` 在这里被定义为 `1.8`,然后在 `<maven.compiler.source>` 和 `<maven.compiler.target>` 中被引用,表示编译源代码和目标 JVM 的版本都是 1.8。

2. **dependencies**:这部分列出了项目所依赖的库。每个 `<dependency>` 标签定义了一个依赖项,包括其 groupId(通常是项目的包名),artifactId(项目名),以及版本号。有些依赖项还定义了 `<scope>`,这决定了依赖在什么阶段可用。例如,`<scope>test</scope>` 表示这个依赖只在测试阶段可用。

- 1. **spring-boot-starter-web**:这是 Spring Boot 的一个启动器,它包含了创建一个基于 Spring MVC 的 Web 应用所需的所有依赖。这意味着你的项目可能是一个 Web 应用。

- 2. **lombok**:Lombok 是一个可以通过注解的方式,使 Java 代码更简洁的库。它提供了一系列的注解,如 `@Data`,`@Getter`,`@Setter` 等,可以自动为类生成 getter 和 setter 方法,以及 `equals()`,`hashCode()` 和 `toString()` 方法。`<scope>provided</scope>` 表示这个依赖在编译和运行时都需要,但在打包成最终的 JAR 或 WAR 文件时,不需要包含进去。

- 3. **spring-boot-starter-test** 和 **junit**:这两个依赖都是用于测试的。`spring-boot-starter-test` 包含了使用 Spring Boot 进行集成测试所需的所有依赖,而 `junit` 是 Java 的一个单元测试框架。这两个依赖的 `<scope>` 都被设置为 `test`,表示它们只在测试阶段可用。

- 4. **tio-boot**: 高性能的 JavaWeb 开发框架

- 5. **jfinal-plugins**:JFinal 框架的插件库。

- 6. **druid**:Druid 是 Alibaba 开发的一个数据库连接池。它提供了强大的监控和扩展功能。

- 7 **mysql-connector-java**:这是 MySQL 的 JDBC 驱动,用于在 Java 应用中连接 MySQL 数据库。

3. **build**:这部分定义了构建项目时要使用的插件和配置。在这里,你使用了 `spring-boot-maven-plugin` 插件,它可以创建一个可执行的 JAR 文件,包含了所有的依赖和应用服务器。

4. **dependencyManagement**:这部分允许你集中管理项目中所有的版本号,避免在每个 `<dependency>` 标签中都要写版本号。在这里,你引入了 `spring-boot-dependencies`,这是一个特殊的依赖,它包含了 Spring Boot 项目中所有可能用到的依赖的版本号。

```xml
<properties>
    <java.version>1.8</java.version>
    <maven.compiler.source>${java.version}</maven.compiler.source>
    <maven.compiler.target>${java.version}</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <spring-boot.version>2.5.6</spring-boot.version>
    <main.class>com.litongjava.spring.boot.tio.boot.demo01.Applicaton</main.class>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <scope>provided</scope>
    </dependency>
    <!-- SpringBoot集成Test -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>com.litongjava</groupId>
      <artifactId>tio-boot</artifactId>
      <version>1.3.1</version>
    </dependency>
    <dependency>
      <groupId>com.litongjava</groupId>
      <artifactId>jfinal-plugins</artifactId>
      <version>1.0.1</version>
    </dependency>
    <!-- 连接池 -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.10</version>
    </dependency>
    <!-- 数据库驱动 -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <!-- Spring Boot -->
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <version>${spring-boot.version}</version>
        <configuration>
          <includeSystemScope>true</includeSystemScope>
          <!--使该插件支持热启动 -->
          <fork>true</fork>
          <mainClass>${main.class}</mainClass>
          <excludeGroupIds>org.projectlombok</excludeGroupIds>
        </configuration>
        <!-- 设置执行目标 -->
        <executions>
          <execution>
            <goals>
              <goal>repackage</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>${spring-boot.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>
```


### 编写代码

#### 启动类

这是一个 Spring Boot 应用的启动类,它同时启动了 SpringApplication 和 TioApplication。:

1. **@SpringBootApplication**:这是一个组合注解,它包含了 `@AConfiguration`,`@EnableAutoConfiguration` 和 `@AComponentScan`。这个注解告诉 Spring Boot 这个类是一个配置类,也是自动配置和组件扫描的起点。

2. **@AComponentScan**:这是一个自定义注解,用于 tio-boot 扫描类

3. **main 方法**:这是 Java 程序的入口点。在这个方法中,首先启动了 SpringApplication,然后又启动了 TioApplication。

   - **SpringApplication.run(Applicaton.class, args)**:这行代码启动了 Spring Boot 应用。它会创建一个 Spring 应用上下文,执行自动配置,然后启动 Spring Boot 的内嵌服务器(如果有的话)。

   - **TioApplication.run(Applicaton.class, newArgs)**:这行代码启动了 TioApplication。在启动之前,它添加了一个参数 `--tio.noServer=true` 到参数列表中。这个参数的目的是告诉 TioApplication 不启动 Tio Boot 的服务器。这样就可以只启动 Spring Boot 的服务器.

4. **计算启动时间**:这段代码计算了从启动 SpringApplication 到启动 TioApplication 的总时间,并打印出来。

```
package com.litongjava.spring.boot.tio.boot.demo01;
import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.litongjava.jfinal.aop.annotation.AComponentScan;
import com.litongjava.tio.boot.TioApplication;
@SpringBootApplication
@AComponentScan
public class Applicaton {
  public static void main(String[] args) {
    long start = System.currentTimeMillis();
    SpringApplication.run(Applicaton.class, args);
    List<String> list = new ArrayList<String>();
    for (int i = 0; i < args.length; i++) {
      list.add(args[i]);
    }
    list.add("--tio.noServer=true");
    String[] newArgs = list.toArray(new String[] {});
    TioApplication.run(Applicaton.class, newArgs);
    long end = System.currentTimeMillis();
    System.out.println(end - start + "(ms)");
  }
}
```


**编写配置类连接数据库**

配置类中包含了连接数据库的设置,使用 DruidPlugin 和 ActiveRecordPlugin。

1. **@AConfiguration**:这是一个 tio-boot 注解,用于标记这个类是一个配置类。

2. **druidPlugin 方法**:这个方法用于创建和配置一个 DruidPlugin 对象,这是一个数据库连接池插件。它使用了 `@ABean(priority = 10)` 注解用于告诉 Tio Boot 在其他 Bean 之前创建这个 Bean。在这个方法中,首先定义了数据库的连接信息,然后创建了一个 DruidPlugin 对象,并启动了它。

3. **activeRecordPlugin 方法**:这个方法用于创建和配置一个 ActiveRecordPlugin 对象,这是一个 数库查询 插件。它使用了 `@ABean` 注解,用于告诉 Tio Boot 这个方法返回的对象应该被管理为一个 Bean。在这个方法中,首先从 Tio Boot 的 Aop 容器中获取了 DruidPlugin 对象,然后创建了一个 ActiveRecordPlugin 对象,并设置了一个新的 OrderedFieldContainerFactory 对象作为其容器工厂,最后启动了它。


```java
package com.litongjava.spring.boot.tio.boot.demo01.config;
import com.litongjava.jfinal.aop.Aop;
import com.litongjava.jfinal.aop.annotation.ABean;
import com.litongjava.jfinal.aop.annotation.AConfiguration;
import com.litongjava.jfinal.plugin.activerecord.ActiveRecordPlugin;
import com.litongjava.jfinal.plugin.activerecord.OrderedFieldContainerFactory;
import com.litongjava.jfinal.plugin.druid.DruidPlugin;
@AConfiguration
public class ActiveRecordPluginConfig {
  @ABean(priority = 10)
  public DruidPlugin druidPlugin() {
    String jdbcUrl = "jdbc:mysql://192.168.3.9:3306/mybatis_plus_study";
    String jdbcUser = "root";
    String jdbcPswd = "robot_123456#";
    DruidPlugin druidPlugin = new DruidPlugin(jdbcUrl, jdbcUser, jdbcPswd);
    druidPlugin.start();
    return druidPlugin;
  }
  @ABean
  public ActiveRecordPlugin activeRecordPlugin() {
    DruidPlugin druidPlugin = Aop.get(DruidPlugin.class);
    ActiveRecordPlugin arp = new ActiveRecordPlugin(druidPlugin);
    arp.setContainerFactory(new OrderedFieldContainerFactory());
    arp.start();
    return arp;
  }
}
```


**编写 UserService 接口**

创建 UserService 接口,定义了获取用户列表的方法。

```java
package com.litongjava.spring.boot.tio.boot.demo01.services;
import java.util.List;
import com.litongjava.jfinal.plugin.activerecord.Record;
public interface UserService {
  List<Record> listAll();
}
```


**编写 UserServiceImpl 实现类**

实现 UserService 接口,通过调用数据库操作获取用户列表。这是一个 tio boot 的 service,使用了@AService 注解

```
package com.litongjava.spring.boot.tio.boot.demo01.services;
import java.util.List;
import com.litongjava.jfinal.aop.annotation.AService;
import com.litongjava.jfinal.plugin.activerecord.Db;
import com.litongjava.jfinal.plugin.activerecord.Record;
@AService
public class UserServiceImpl implements UserService {
  public List<Record> listAll() {
    return Db.findAll("user");
  }
}
```


**编写 UserController**

在 Controller 中调用 UserService,返回获取的用户数据。这个是 spring 的 controller 使用了@RestController,在方法中使用了 Aop.get 方法从 tio boot 的 aop 容器中获取一个 bean

```java
package com.litongjava.spring.boot.tio.boot.demo01.AController;
import java.util.List;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.litongjava.jfinal.aop.Aop;
import com.litongjava.jfinal.plugin.activerecord.Record;
import com.litongjava.spring.boot.tio.boot.demo01.services.UserService;
@RestController
@RequestMapping("/user")
public class UserController {
  @RequestMapping("/listAll")
  public List<Record> listAll(){
    return Aop.get(UserService.class).listAll();
  }
}
```


**访问接口测试**

启动项目后,访问接口测试:http://localhost:10510/user/listAll

**打包成 fastjar**

使用以下命令将项目打包成 fastjar:

```bash
mvn clean install -DskipTests -Dgpg.skip
```

**启动 fastjar**

使用以下命令启动 fastjar:

```bash
java -jar target\spring-boot-2.5.6-tio-boot-1.0.jar
```

再次访问接口测试:http://localhost:10510/user/listAll

### 测试 UserService

编写一个测试类 UserServiceTest,测试 UserService

```
package com.litongjava.spring.boot.tio.boot.demo01.services;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import com.litongjava.jfinal.aop.Aop;
import com.litongjava.jfinal.plugin.activerecord.Record;
import com.litongjava.spring.boot.tio.boot.demo01.Applicaton;
import com.litongjava.tio.boot.TioApplication;
public class UserServiceTest {
  @Before
  public void before() {
    List<String> list = new ArrayList<String>();
    list.add("--tio.noServer=true");
    String[] args = list.toArray(new String[] {});
    TioApplication.run(Applicaton.class, args);
  }
  @Test
  public void test() {
    UserService userService = Aop.get(UserService.class);
    List<Record> listAll = userService.listAll();
    System.out.println(listAll);
  }
}
```


这是一个使用 JUnit 框架编写的测试类,用于测试 `UserService` 类的功能。

1. **@Before**:这是一个 JUnit 注解,表示 `before()` 方法会在每个测试方法执行之前运行。在这个方法中,它启动了 TioApplication,但是通过添加 `--tio.noServer=true` 参数,不启动 Tio Boot 的服务器。

2. **@Test**:这是一个 JUnit 注解,表示 `test()` 方法是一个测试方法。在这个方法中,首先从 tio-boot aop 容器中获取了 `UserService` 对象,然后调用了 `listAll()` 方法获取所有的用户记录,并打印出来。

这个测试类的目的是验证 `UserService` 类的 `listAll()` 方法是否能正确地获取所有的用户记录。


评论区