## 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()` 方法是否能正确地获取所有的用户记录。