相关文章
前言
上一次发布关于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哦!
源码下载