摆脱Spring依赖症——使用Vert.x 4与JOOQ打造REST服务脚手架项目

4 篇文章 2 订阅

相关文章

Vert.x-Web与SpringBoot整合



前言

上一次发布关于Vert.x的文章已经是两年多以前了,写《Vert.x-Web与SpringBoot整合》的意图主要是帮助想尝试Vert.x又很难快速从spring框架的开发模式中转变过来的同学,而这一篇文章将完全剥离spring,帮助大家体验纯粹的Vert.x之美。正好Vert.x 4.0大本版已经正式发布,我们就用4.0来撸一个脚手架项目。


一、项目框架

二、项目目录结构

目录结构

项目包含core和web两个模块:core模块为框架核心模块,核心模块不包含任何业务代码,主要有自定义注解、工具、基础类和框架verticle;web模块为网络模块,由于是demo项目,没有再具体划分,因此该模块包括业务代码、数据源相关代码以及持久化层相关框架代码,同时web模块也包含项目的入口。


三、项目构建

项目使用maven构建,JDK版本为1.8。下面贴上pom.xml

  • core
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>vertx-web-jooq-demo</artifactId>
        <groupId>com.xxx</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <version>${project.parent.version}</version>
    <artifactId>core</artifactId>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <vertx.version>4.0.0</vertx.version>
        <org.reflections.version>0.9.12</org.reflections.version>
        <lombok.version>1.18.12</lombok.version>
        <slf4j.version>1.7.25</slf4j.version>
        <commons-lang3.version>3.4</commons-lang3.version>
        <jackson.version>2.11.3</jackson.version>
        <logback.version>1.2.3</logback.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>io.vertx</groupId>
                <artifactId>vertx-dependencies</artifactId>
                <version>${vertx.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-core</artifactId>
        </dependency>
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-web</artifactId>
        </dependency>
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-service-proxy</artifactId>
        </dependency>
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-codegen</artifactId>
        </dependency>
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.reflections</groupId>
            <artifactId>reflections</artifactId>
            <version>${org.reflections.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>${commons-lang3.version}</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

在核心模块引入Vert.x相关框架,值得一提的是reflections是非常好用的反射框架,可以帮我们解决批量扫描操作各种业务类的问题。

  • web
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>vertx-web-jooq-demo</artifactId>
        <groupId>com.xxx</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <version>${project.parent.version}</version>
    <artifactId>web</artifactId>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <logback.version>1.2.3</logback.version>
        <slf4j.version>1.7.25</slf4j.version>
        <vertx-jooq.version>6.1.0</vertx-jooq.version>
        <hikari-cp.version>3.4.5</hikari-cp.version>
        <mysql-driver.version>5.1.37</mysql-driver.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.xxx</groupId>
            <artifactId>core</artifactId>
            <version>${project.parent.version}</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${logback.version}</version>
            <exclusions>
                <exclusion>
                    <artifactId>slf4j-api</artifactId>
                    <groupId>org.slf4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.github.jklingsporn</groupId>
            <artifactId>vertx-jooq-classic-jdbc</artifactId>
            <version>${vertx-jooq.version}</version>
        </dependency>
        <dependency>
            <groupId>io.github.jklingsporn</groupId>
            <artifactId>vertx-jooq-generate</artifactId>
            <version>${vertx-jooq.version}</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql-driver.version}</version>
        </dependency>
        <dependency>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
            <version>${hikari-cp.version}</version>
            <exclusions>
                <exclusion>
                    <artifactId>slf4j-api</artifactId>
                    <groupId>org.slf4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <directory>${project.basedir}/target</directory>
        <outputDirectory>${project.build.directory}/classes</outputDirectory>
        <finalName>${project.artifactId}-${project.version}</finalName>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <annotationProcessors>
                        <annotationProcessor>io.vertx.codegen.CodeGenProcessor</annotationProcessor>
                    </annotationProcessors>
                    <generatedSourcesDirectory>
                        ${project.basedir}/src/main/generated
                    </generatedSourcesDirectory>
                    <compilerArgs>
                        <arg>-AoutputDirectory=${project.basedir}/src/main</arg>
                    </compilerArgs>
                </configuration>
            </plugin>
            <!--            <plugin>-->
            <!--                <groupId>org.apache.maven.plugins</groupId>-->
            <!--                <artifactId>maven-shade-plugin</artifactId>-->
            <!--                <version>2.3</version>-->
            <!--                <executions>-->
            <!--                    <execution>-->
            <!--                        <phase>package</phase>-->
            <!--                        <goals>-->
            <!--                            <goal>shade</goal>-->
            <!--                        </goals>-->
            <!--                        <configuration>-->
            <!--                            <transformers>-->
            <!--                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">-->
            <!--                                    <manifestEntries>-->
            <!--                                        <Main-Class>io.vertx.core.Launcher</Main-Class>-->
            <!--                                        <Main-Verticle>com.xxx.verticle.MainVerticle</Main-Verticle>-->
            <!--                                    </manifestEntries>-->
            <!--                                </transformer>-->
            <!--                                <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>-->
            <!--                            </transformers>-->
            <!--                            <artifactSet>-->
            <!--                            </artifactSet>-->
            <!--                            <outputFile>${project.build.directory}/${project.artifactId}-${project.version}-fat.jar</outputFile>-->
            <!--                        </configuration>-->
            <!--                    </execution>-->
            <!--                </executions>-->
            <!--            </plugin>-->
            <!--打包jar-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <!--不打包资源文件-->
                    <excludes>
                        <exclude>*.**</exclude>
                        <exclude>*/*.xml</exclude>
                    </excludes>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <!--MANIFEST.MF 中 Class-Path 加入前缀-->
                            <classpathPrefix>lib/</classpathPrefix>
                            <!--jar包不包含唯一版本标识-->
                            <useUniqueVersions>false</useUniqueVersions>
                            <!--指定入口类-->
                            <mainClass>com.xxx.AppMain</mainClass>
                        </manifest>
                        <manifestEntries>
                            <!--MANIFEST.MF 中 Class-Path 加入资源文件目录-->
                            <Class-Path>./resources/</Class-Path>
                        </manifestEntries>
                    </archive>
                    <outputDirectory>${project.build.directory}</outputDirectory>
                </configuration>
            </plugin>
            <!--拷贝依赖 copy-dependencies-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>
                                ${project.build.directory}/lib/
                            </outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <!--拷贝资源文件 copy-resources-->
            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-resources</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-resources</goal>
                        </goals>
                        <configuration>
                            <resources>
                                <resource>
                                    <directory>src/main/resources</directory>
                                </resource>
                            </resources>
                            <outputDirectory>${project.build.directory}/resources</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

web模块引入了core模块,logback日志框架,HikariCP数据库连接池,以及vertx-jooq持久化框架等。由于个人习惯打包插件引用较多,主要是用于打包后将lib与resources从jar包中剥离,便于打包后的配置修改以及局部更新,如果使用fat-jar那么可以直接使用注释掉的maven-shade-plugin插件,使用io.vertx.core.Launcher作为程序入口还需要自行编写MainVerticle类。


四、代码详解

1.配置

配置文件在web模块的resources/conf中,文件名为config.json,这样配置文件就可以被Vert.x Config自动获取。文件如下

{
  "dev": {
    "server": {
      "port": 8088,
      "contextPath": "/demo"
    },
    "vertx": {
      "eventLoopPoolSize": 2,
      "workerPoolSize": 20,
      "blockedThreadCheckInterval": 999999999,
      "maxEventLoopExecuteTime": 999999999,
      "maxWorkerExecuteTime": 999999999,
      "eventBusOptions": {
        "connectTimeout": 1800000
      }
    },
    "dataSource": {
      "driverClassName": "com.mysql.jdbc.Driver",
      "jdbcUrl": "jdbc:mysql://localhost:3306/demo?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8&tinyInt1isBit=false",
      "username": "root",
      "password": "******",
      "maximumPoolSize": 8
    },
    "custom": {
      "asyncServiceInstances": 1,
      "routerLocations": "com.xxx.web.rest",
      "handlerLocations": "com.xxx.web.service",
      "daoLocations": "com.xxx.web.jooq.tables.daos"
    }
  },
  "prod": {
    "server": {
      "port": 8108,
      "contextPath": "/demo"
    },
    "http": {
      "maxWebSocketFrameSize": 1000000
    },
    "dataSource": {
      "driverClassName": "com.mysql.jdbc.Driver",
      "jdbcUrl": "jdbc:mysql://localhost:3306/demo?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8&tinyInt1isBit=false",
      "username": "root",
      "password": "******",
      "maximumPoolSize": 16
    },
    "vertx": {
      "eventLoopPoolSize": 4,
      "workerPoolSize": 40,
      "eventBusOptions": {
        "connectTimeout": 6000
      }
    },
    "custom": {
      "asyncServiceInstances": 1,
      "routerLocations": "com.xxx.web.rest",
      "handlerLocations": "com.xxx.web.service",
      "daoLocations": "com.xxx.web.jooq.tables.daos"
    }
  }
}

dev为开发环境配置,prod为生产环境配置,有更多环境可以自行添加,json文件配置十分灵活没有yml中的一些限制。配置中保留了springboot中的一些习惯,例如server配置下有http服务器端口号和项目根路径配置。vertx配置主要是把各种超时时间延长以便开发时进断点调试,数据源配置不赘述,custom自定义配置中asyncServiceInstances用来控制发布的服务层verticle的实例数,其他为reflections的扫描包路径。

2.入口

程序入口是web模块下的AppMain类

package com.xxx.web;

import com.xxx.core.handlerfactory.RouterHandlerFactory;
import com.xxx.core.util.DeployVertxServer;
import com.xxx.core.util.VertxHolder;
import com.xxx.web.dao.DaoConfigurationHolder;
import com.xxx.web.dao.JooqDaoHolder;
import com.xxx.web.datasource.DataSourceHolder;
import io.vertx.config.ConfigRetriever;
import io.vertx.core.Vertx;
import io.vertx.core.VertxOptions;
import io.vertx.core.json.JsonObject;
import io.vertx.core.shareddata.LocalMap;
import io.vertx.core.shareddata.SharedData;
import io.vertx.ext.web.Router;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;



/**
 * 程序入口
 */
public class AppMain {

    private static final Logger LOGGER = LoggerFactory.getLogger(AppMain.class);

    private static String env = "dev";

    public static void main(String[] args) {
        if (args.length > 0) {
            env = args[0];
        }
        Vertx tempVertx = Vertx.vertx();
        ConfigRetriever retriever = ConfigRetriever.create(tempVertx);
        //获取conf/config.json中的配置
        retriever.getConfig(ar -> {
            tempVertx.close();
            JsonObject result = ar.result();
            LOGGER.info("配置读取成功:" + result.encode());
            //默认读取dev开发环境配置
            JsonObject envConfig = result.getJsonObject(env);
            JsonObject serverConfig = envConfig.getJsonObject("server");
            JsonObject vertxConfig = envConfig.getJsonObject("vertx");
            JsonObject dataSourceConfig = envConfig.getJsonObject("dataSource");
            JsonObject customConfig = envConfig.getJsonObject("custom");
            Vertx vertx = Vertx.vertx(new VertxOptions(vertxConfig));
            VertxHolder.init(vertx);
            //配置保存在共享数据中
            SharedData sharedData = vertx.sharedData();
            LocalMap<String, Object> localMap = sharedData.getLocalMap("demo");
            localMap.put("env", env);
            localMap.put("envConfig", envConfig);
            //先初始化再发布Http服务
            vertx.executeBlocking(p -> {
                //顺序不能乱
                try {
                    //初始化数据源
                    DataSourceHolder.init(dataSourceConfig);
                    //初始化jooq dao配置
                    DaoConfigurationHolder.init();
                    //初始化dao
                    JooqDaoHolder.init(customConfig.getString("daoLocations"));
                    p.complete();
                } catch (Exception e) {
                    p.fail(e);
                }
            }).onComplete(ar2 -> {
                if (ar2.succeeded()) {
                    Router router = new RouterHandlerFactory(customConfig.getString("routerLocations"), serverConfig.getString("contextPath")).createRouter();
                    DeployVertxServer.startDeploy(router, customConfig.getString("handlerLocations"), serverConfig.getInteger("port"),
                            customConfig.getInteger("asyncServiceInstances"));
                } else {
                    LOGGER.error(ar.cause().getMessage(), ar.cause());
                }
            });
        });
    }
}

环境变量env默认为dev,可以通过java启动参数设置修改。
创建临时Vertx 读取config.json文件,然后销毁,再根据配置创建新的Vertx实例,并保存在VertxHolder中便于后续获取。然后将配置信息以及当前运行环境保存在SharedData中便于后续使用。
根据配置文件初始化数据源、jooq的daoConfiguration以及自定义的JooqDaoHolder,初始化成功后,对api进行扫描并构建Router对象,然后根据配置发布http服务verticle以及将服务层注册到EventBus。

3.代码生成

代码生成类JooqGenerator在web模块的test中,vertx-jooq框架生成的代码包括实体类和dao层代码等,具体可以在文章末尾下载源码查看或直接去github搜索vertx-jooq。

4.业务代码

在本框架下,需要开发者自行编写的代码主要是web层与service层代码。web层代码示例如下

package com.xxx.web.rest;

import com.xxx.core.annotaions.RouteHandler;
import com.xxx.core.annotaions.RouteMapping;
import com.xxx.core.annotaions.RouteMethod;
import com.xxx.core.base.BaseRestApi;
import com.xxx.core.model.JsonResult;
import com.xxx.core.util.AsyncServiceUtil;
import com.xxx.core.util.ParamUtil;
import com.xxx.web.service.OrderService;
import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext;
import org.apache.commons.lang3.StringUtils;

@RouteHandler(value = "orderApi")
public class OrderApi extends BaseRestApi {

    private OrderService orderService = AsyncServiceUtil.getAsyncServiceInstance(OrderService.class);

    @RouteMapping(value = "/findOrderById/:orderId", method = RouteMethod.GET)
    public Handler<RoutingContext> findOrderById() {
        return ctx -> {
            String orderId = ctx.pathParam("orderId");
            if (StringUtils.isBlank(orderId)) {
                sendError(400, ctx);
            } else {
                orderService.findOrderAndItemById(Long.valueOf(orderId), ar -> {
                    if (ar.succeeded()) {
                        JsonObject product = ar.result();
                        fireJsonResponse(ctx, new JsonResult(product));
                    } else {
                        fireErrorJsonResponse(ctx, ar.cause().getMessage());
                    }
                });
            }
        };
    }

    @RouteMapping(value = "/listOrderPage/:current", method = RouteMethod.GET)
    public Handler<RoutingContext> listOrderPage() {
        return ctx -> {
            String current = ctx.pathParam("current");
            if (StringUtils.isBlank(current)) {
                sendError(400, ctx);
            } else {
                orderService.listOrderPage(Integer.valueOf(current), ar -> {
                    if (ar.succeeded()) {
                        fireJsonResponse(ctx, new JsonResult(ar.result()));
                    } else {
                        fireErrorJsonResponse(ctx, ar.cause().getMessage());
                    }
                });
            }
        };
    }

    @RouteMapping(value = "/deleteById/:orderId", method = RouteMethod.DELETE)
    public Handler<RoutingContext> deleteById() {
        return ctx -> {
            String orderId = ctx.pathParam("orderId");
            if (StringUtils.isBlank(orderId)) {
                sendError(400, ctx);
            } else {
                orderService.deleteOrder(Long.valueOf(orderId), ar -> {
                    if (ar.succeeded()) {
                        fireJsonResponse(ctx, new JsonResult(ar.result()));
                    } else {
                        fireErrorJsonResponse(ctx, ar.cause().getMessage());
                    }
                });
            }
        };
    }

    @RouteMapping(value = "/saveOrder", method = RouteMethod.POST)
    public Handler<RoutingContext> saveOrder() {
        return ctx -> {
            JsonObject params = ParamUtil.getRequestParams(ctx);
            orderService.saveOrder(params, ar -> {
                if (ar.succeeded()) {
                    fireJsonResponse(ctx, new JsonResult(ar.result()));
                } else {
                    fireErrorJsonResponse(ctx, ar.cause().getMessage());
                }
            });
        };
    }
}

web层代码乍看之下和SpringMVC极为相似,颇有些亲切感。框架会根据routerLocations配置扫描@RouteHandler和@RouteMapping注解并构建Router对象。该示例演示了CRUD的REST服务写法。

service层代码包括接口和实现类

package com.xxx.web.service;

import io.vertx.codegen.annotations.ProxyGen;
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;

/**
 * @author Ian
 * @date 2021/1/11 9:25
 */
@ProxyGen
public interface OrderService {

    void findOrderAndItemById(Long orderId, Handler<AsyncResult<JsonObject>> resultHandler);

    void listOrderPage(Integer current, Handler<AsyncResult<JsonObject>> resultHandler);

    void deleteOrder(Long orderId, Handler<AsyncResult<Void>> resultHandler);

    void saveOrder(JsonObject json, Handler<AsyncResult<JsonObject>> resultHandler);
}
package com.xxx.web.service.impl;

import com.xxx.core.base.BaseAsyncService;
import com.xxx.core.util.IdWorker;
import com.xxx.web.dao.JooqDaoHolder;
import com.xxx.web.jooq.tables.daos.TOrderDao;
import com.xxx.web.jooq.tables.daos.TOrderItemDao;
import com.xxx.web.jooq.tables.pojos.TOrder;
import com.xxx.web.jooq.tables.pojos.TOrderItem;
import com.xxx.web.jooq.tables.records.TOrderRecord;
import com.xxx.web.service.OrderService;
import com.xxx.web.util.PojoUtil;
import io.vertx.core.AsyncResult;
import io.vertx.core.CompositeFuture;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import org.jooq.SortField;
import org.jooq.UpdateQuery;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * @author Ian
 * @date 2021/1/11 9:55
 */
public class OrderServiceImpl extends BaseAsyncService implements OrderService {

    private TOrderDao orderDao = JooqDaoHolder.getDaoInstance(TOrderDao.class);

    private TOrderItemDao orderItemDao = JooqDaoHolder.getDaoInstance(TOrderItemDao.class);

    @Override
    public void findOrderAndItemById(Long orderId, Handler<AsyncResult<JsonObject>> resultHandler) {
        Future<TOrder> future1 = orderDao.findOneById(orderId);
        Future<List<TOrderItem>> future2 = orderItemDao.findManyByOrderId(Collections.singleton(orderId));
        CompositeFuture.all(future1, future2).onSuccess(ar -> {
            if (ar.succeeded()) {
                TOrder tOrder = ar.resultAt(0);
                if (null == tOrder) {
                    resultHandler.handle(Future.succeededFuture(null));
                } else {
                    JsonObject result = tOrder.toJson();
                    List<TOrderItem> tOrderItemList = ar.resultAt(1);
                    result.put("children", PojoUtil.convertToArray(tOrderItemList));
                    resultHandler.handle(Future.succeededFuture(result));
                }
            } else {
                handleException(ar.cause(), resultHandler);
            }
        });
    }

    @Override
    public void listOrderPage(Integer current, Handler<AsyncResult<JsonObject>> resultHandler) {
        SortField<?> sortField = com.xxx.web.jooq.tables.TOrder.T_ORDER.CREATE_TIME.desc();
        JsonObject page = new JsonObject();
        page.put("current", current);
        page.put("size", 10);
        int offset = (current - 1) * 10;
        orderDao.queryExecutor().query(dslContext ->
                dslContext.selectCount().from(com.xxx.web.jooq.tables.TOrder.T_ORDER)
        ).compose(queryResult -> {
            page.put("total", queryResult.get(0, int.class));
            return orderDao.findManyByCondition(null, 10, offset, sortField);
        }).onComplete(ar -> {
            if (ar.succeeded()) {
                page.put("rows", PojoUtil.convertToArray(ar.result()));
                resultHandler.handle(Future.succeededFuture(page));
            } else {
                handleException(ar.cause(), resultHandler);
            }
        });
    }

    @Override
    public void deleteOrder(Long orderId, Handler<AsyncResult<Void>> resultHandler) {
        //无事务
//        orderDao.deleteById(orderId).compose(integer ->
//                orderItemDao.deleteByCondition(com.xxx.web.jooq.tables.TOrderItem.T_ORDER_ITEM.ORDER_ID.eq(orderId))
//        ).onComplete(ar -> {
//            if (ar.succeeded()) {
//                resultHandler.handle(Future.succeededFuture());
//            } else {
//                handleException(ar.cause(), resultHandler);
//            }
//        });
        //有事务
        orderDao.queryExecutor().executeAny(dslContext -> {
                    dslContext.transaction(c -> {
                        c.dsl().deleteFrom(com.xxx.web.jooq.tables.TOrder.T_ORDER)
                                .where(com.xxx.web.jooq.tables.TOrder.T_ORDER.ID.eq(orderId)).execute();
                        c.dsl().deleteFrom(com.xxx.web.jooq.tables.TOrderItem.T_ORDER_ITEM)
                                .where(com.xxx.web.jooq.tables.TOrderItem.T_ORDER_ITEM.ORDER_ID.eq(orderId)).execute();
                    });
                    return 0;
                }
        ).onComplete(ar -> {
            if (ar.succeeded()) {
                resultHandler.handle(Future.succeededFuture());
            } else {
                handleException(ar.cause(), resultHandler);
            }
        });
    }

    @Override
    public void saveOrder(JsonObject json, Handler<AsyncResult<JsonObject>> resultHandler) {
        TOrder tOrder = new TOrder(json);
        Future<Integer> future;
        if (json.containsKey("id") && null != json.getValue("id")) {
            future = orderDao.queryExecutor().executeAny(dslContext -> {
                        UpdateQuery<TOrderRecord> updateQuery = dslContext.updateQuery(com.xxx.web.jooq.tables.TOrder.T_ORDER);
                        Map<String, Object> map = new HashMap<>();
                        if (null != tOrder.getName()) {
                            map.put("name", tOrder.getName());
                        }
                        if (null != tOrder.getUserName()) {
                            map.put("user_name", tOrder.getUserName());
                        }
                        updateQuery.addValues(map);
                        updateQuery.addConditions(com.xxx.web.jooq.tables.TOrder.T_ORDER.ID.eq(tOrder.getId()));
                        return updateQuery.execute();
                    }
            );
            //修改所有字段
//            future = orderDao.update(tOrder);
        } else {
            tOrder.setId(IdWorker.getId()).setCreateTime(LocalDateTime.now());
            future = orderDao.insert(tOrder);
        }
        future.onComplete(ar -> {
            if (ar.succeeded()) {
                resultHandler.handle(Future.succeededFuture(tOrder.toJson()));
            } else {
                handleException(ar.cause(), resultHandler);
            }
        });
    }
}

需要注意的有两点:接口需要加上@ProxyGen注解,这样Vert.x Service Proxy框架就会自动生成服务代理类(注意在入口类同级添加package-info.java);实现类需要继承BaseAsyncService类,这样才能被扫描到并注册到EventBus。


总结

异步之于Vert.x既是优点也是缺点,在高性能之下,对开发者编程能力也有着比同步编程更高的要求,希望本文能够对想要跳出spring体系看看外面的世界的同学们有所帮助,下面是源码,记得star哦!
源码下载

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 14
    评论
### 回答1: Vert.x 是一个基于 JVM 的开源、高性能、响应式应用程序框架,用于构建事件驱动的应用程序。阿里巴巴使用 Vert.x 构建了很多应用程序,如阿里云数据库的 Hologres 等。 Vert.x 的主要特点包括: 1. 高性能:Vert.x 可以处理大量并发连接,而不会出现阻塞情况,从而提供高性能的响应式应用程序。 2. 响应式:Vert.x 支持异步编程模型,使得开发响应式应用程序更加容易。 3. 多语言支持:Vert.x 可以使用多种语言进行开发,包括 Java、Kotlin、Groovy、JavaScript 等。 4. 模块化:Vert.x 拥有一个丰富的模块库,使得开发人员可以方便地集成第三方组件和库。 5. 部署简单:Vert.x 的应用程序可以轻松地部署到云、容器和传统服务器上。 阿里巴巴使用 Vert.x 来构建高性能、高可用性的微服务应用程序。例如,阿里云数据库的 Hologres 就是使用 Vert.x 开发的。Vert.x 可以帮助阿里巴巴构建高性能、响应式、可扩展的应用程序,从而提高用户的体验。 ### 回答2: 阿里巴巴是一家中国的互联网巨头公司,它在多个领域进行了项目开发,其中就包括了使用Vert.xVert.x是一个开源的、事件驱动的应用程序框架,它可以用于构建高性能、可伸缩、分布式的应用程序。阿里巴巴在使用Vert.x时,主要应用于以下几个项目中: 1. 分布式应用程序:阿里巴巴在架构设计上广泛采用微服务架构,而Vert.x提供的分布式事件总线和消息传递机制能够帮助阿里巴巴实现不同微服务之间的通信和协作。 2. 实时数据处理:阿里巴巴的许多业务都需要实时处理大量的数据,比如电商平台的实时订单处理、物流跟踪等。Vert.x的高性能和低延迟的特点能够满足这些实时处理的需求。 3. 规模化应用程序:随着阿里巴巴业务的快速发展,应用程序需要能够处理更大规模的请求和并发用户。Vert.x提供的事件驱动和非阻塞IO模型,可以帮助阿里巴巴构建高性能、可伸缩的应用程序。 4. 弹性和容错性:Vert.x框架提供了容错和弹性的机制,这对于阿里巴巴的系统来说是非常重要的。当系统出现故障或部分节点失效时,Vert.x可以自动进行故障转移,保证服务的可用性和稳定性。 综上所述,阿里巴巴在使用Vert.x时,主要应用于分布式应用程序、实时数据处理、规模化应用程序和弹性与容错性的需求上。通过利用Vert.x的优势,阿里巴巴的项目能够更好地满足业务需求,并提供高性能和可靠性的服务。 ### 回答3: 阿里巴巴在使用Vert.x进行了一些项目开发,其中一个主要的应用是实现分布式系统的高性能异步通信。Vert.x是一个基于事件驱动的开发工具包,适用于构建高性能的网络应用程序。 阿里巴巴的部门和团队使用Vert.x来构建分布式系统的核心组件,以提高系统的性能和可伸缩性。通过使用Vert.x,可以将系统的各个部分拆分为独立的微服务,并使用事件驱动的方式进行通信和协作。Vert.x的异步非阻塞特性可以有效地处理高并发的网络请求,提高系统的响应速度。 除了构建核心组件,阿里巴巴还使用Vert.x来开发一些实时的监控和日志分析工具。这些工具可以在系统运行时实时收集和分析各种指标和日志数据,以帮助开发人员快速定位和解决问题。Vert.x的事件驱动模型和高性能的特性使得这些工具可以在高并发的情况下保持稳定和高效。 此外,阿里巴巴还使用Vert.x来构建一些服务网关和API网关。服务网关用于将不同服务之间的通信进行统一管理和路由,API网关则用于对外提供统一的API接口。通过使用Vert.x,这些网关可以高效地处理大量的请求,并提供稳定和可靠的服务。 综上所述,阿里巴巴使用Vert.x进行了一些项目开发,包括分布式系统的高性能异步通信、实时监控和日志分析工具、服务网关和API网关等。这些项目的应用范围广泛,并能够满足阿里巴巴在高并发和高性能方面的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值