vert.x 默认是没有像spring的依赖注入的,需要自己结合vertx-service-proxy插件可以实现类似的效果。本文引用项目为基础《Vert.x 4 Web应用初识》
maven pom.xml 引入依赖
<!--服务代理-->
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-service-proxy</artifactId>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-codegen</artifactId>
<scope>provided</scope>
<classifier>processor</classifier>
</dependency>
<!--反射工具-->
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>${reflections.version}</version>
</dependency>
<!--/服务代理-->
提示:JDK 17环境+idea 环境引入上方配置后无需在build->maven-compiler-plugin插件增加配置即可使用。反而因为配置了annotationProcessor会导致报错,但是可以通过下面配置进行修改默认的生成代码路径
<generatedSourcesDirectory>
${project.basedir}/src/main/generated
</generatedSourcesDirectory>
<compilerArgs>
<arg>-AoutputDirectory=${project.basedir}/src/main</arg>
</compilerArgs>
修改了路径记得把清理插件配置上,清理这个路径
<!-- 删除生成的文件 -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<filesets>
<fileset>
<directory>${project.basedir}/src/main/generated</directory>
</fileset>
</filesets>
</configuration>
</plugin>
默认插件生成的代理源码在这里
并且源码提示不要修改
所以,默认最好,不要修改generated的代码输出位置!!!!
UserService
代码聚焦
@ProxyGen
public interface UserService {
/**
* 根据id获取用户信息
* @param id 用户id
* @param handler 一部处理
*/
void getUserById(Integer id, Handler<AsyncResult<JsonObject>> handler);
}
vertx service proxy 必须创建包信息指定services(服务层)所在包,因为需要编译时候生成一些代码。
在Service这个包下面创建包信息 package-info.java
内容如下:
@ModuleGen(name = "proxy",groupPackage = "com.demo.vertx.vertx_demo.service")
package com.demo.vertx.vertx_demo.service;
import io.vertx.codegen.annotations.ModuleGen;
说明:
基础service提供了一些每个service都有的方法,就好比spring时代的BaseController之类的效果。
AbstractAsyncService 代码参考:
@Slf4j
public abstract class AbstractAsyncService {
/**
* 服务注册地址
* @return 注册地址
*/
public String registerAddress(){
String className = this.getClass().getName();
return className.substring(0, className.lastIndexOf("Impl")).replace(".impl", "");
}
/**
* 获取注册接口类
* @return 获取注册接口类
* @throws ClassNotFoundException 异常
*/
@SuppressWarnings({"rawtypes"})
public Class registerInterface() throws ClassNotFoundException {
String className = this.getClass().getName();
return Class.forName(className.substring(0, className.lastIndexOf("Impl")).replace(".impl", ""));
}
/**
* 异常处理
* @param throwable 异常
* @param handler 处理器
* @param <T> 泛型入参
*/
protected <T> void handlerException(Throwable throwable, Handler<AsyncResult<T>> handler){
log.error(throwable.getMessage(), throwable);
handler.handle(ServiceException.fail(1, throwable.getMessage()));
}
}
主要三个方法,前两个是用来注册服务时候用的,最后一个是做了个通用异常处理。
提示:考虑到要用泛型,注册地址取巧直接使用的service全名(可能导致一个service接口多个实现类问题,但是一般也是一个对一个用)
接口实现继承了AbstractAsyncService 和实现了用户服务
代码示例:
public class UserServiceImpl extends AbstractAsyncService implements UserService {
@Override
public void getUserById(Integer id, Handler<AsyncResult<JsonObject>> handler) {
if (id == null) {
handler.handle(Future.failedFuture("id is null"));
} else {
if (id < 0) {
handler.handle(Future.failedFuture("id is negative"));
}
JsonObject user = new JsonObject()
.put("id", id)
.put("name", "user")
.put("age", 20)
.put("gender", "male")
.put("birthday", "2023-01-01")
;
handler.handle(Future.succeededFuture(user));
}
}
}
代码聚焦
/**
* 注册服务
* @param pageNames 扫描包路径下面的服务进行注册 eg:<code>com.demo.vertx.vertx_demo.service</code>
* <p>
* 更多参考:<a href="https://vertx.io/docs/vertx-service-proxy/java/">vertx-service-proxy</a>
* </p>
*
*/
@SuppressWarnings({"unchecked","rawtypes"})
private void registerServices(String pageNames){
Set<Class<? extends AbstractAsyncService>> services =
ReflectionUtils.getReflections(pageNames).getSubTypesOf(AbstractAsyncService.class);
ServiceBinder serviceBinder = new ServiceBinder(vertx);
if (!services.isEmpty()) {
try {
for (Class<? extends AbstractAsyncService> service : services) {
AbstractAsyncService serviceInstance = service.getDeclaredConstructor().newInstance();
//AbstractAsyncService#registerAddress获取注册地址/ID
//取巧:注册地址=接口全名
String registerAddress = (String) service.getMethod("registerAddress").invoke(serviceInstance);
//AbstractAsyncService#registerInterface获取注册类
Class registerInterfaceClass = (Class) service.getMethod("registerInterface").invoke(serviceInstance);
//注册
serviceBinder
.setAddress(registerAddress)
.register(registerInterfaceClass, serviceInstance);
}
} catch (Exception e) {
log.error("registerServices error : {}", e.getMessage());
}
}
}
通过泛型注册服务到vertx
//获取用户服务(注意:必须先注册再拿注册服务要放到前面)
UserService userService = AsyncServiceUtils.getAsyncServiceInstance(UserService.class, vertx);
router.get("/user/:id").handler(ctx->{
String id = ctx.pathParam("id");
userService.getUserById(Integer.parseInt(id),ar->{
if(ar.succeeded()){
ctx.json(ar.result());
}else {
ctx.response()
.putHeader("content-type", "application/json")
.setStatusCode(500)
.end(ar.cause().getMessage());
}
});
});
启动项目,使用postman工具调用接口尝试
入参为负数,返回结果提示id 是服务HTTP状态码500 与预期结果一致。
输入正确入参,返回服务模拟的数据,与预期结果一致。
至此我们完成了服务代理和服务调用的技能学习。
http://blog.xqlee.com/article/2408141956052450.html