【Java】初学Vert.x(3)

5. 路由配置+接口发布

在上一节中我们也提到了使用递归遍历的方式将所有的 AbstractVerticle 类的继承实现给部署到服务里面,如下图:

...

List<Class<Verticle>> clazzSet = ReflectUtil.getVerticleClasses(CommonConstants.ROOT_LEVEL, true);
List<CompletableFuture<Void>> futures =
    clazzSet.stream().filter(clazz -> !clazz.getSimpleName().contains(VTX_VERTICLE))
        .map(clazz -> CompletableFuture.runAsync(() -> vtx.deployVerticle(clazz, deploymentOptions)))
        .collect(Collectors.toList());
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

...

而我们的路由配置类也继承了 AbstractVerticle 的因此也会被自动部署,如下图:

public class RouterConfig extends AbstractVerticle {
    
    

    private static final Logger LOGGER = LogManager.getLogger(RouterConfig.class);
    private Set<HttpMethod> hmSet = new HashSet<>();

    // 服务端口
    @PropLoader(key = "server.port")
    private int port;

    // 接口 header 设定
    @PropLoader(key = "server.http.header")
    private List<String> httpHeader;

    /**
     * 
     * @MethodName: setupHttpMethod
     * @Description: 设定服务支持那些 http 方法
     * @author yuanzhenhui void
     * @date 2023-07-17 09:26:46
     */
    @PostConstruct
    public void setupHttpMethod() {
    
    
        hmSet.add(HttpMethod.GET);
        hmSet.add(HttpMethod.POST);
        hmSet.add(HttpMethod.DELETE);
        hmSet.add(HttpMethod.PATCH);
        hmSet.add(HttpMethod.OPTIONS);
        hmSet.add(HttpMethod.PUT);
    }

    /**
     * 
     * @MethodName: setupAllowedHeaders
     * @Description: 允许访问的头属性
     * @author yuanzhenhui
     * @return Set<String>
     * @date 2023-04-13 04:28:45
     */
    private Set<String> setupAllowedHeaders() {
    
    
        Set<String> allowedHeaders = new HashSet<>();
        if (httpHeader != null) {
    
    
            allowedHeaders.addAll(httpHeader);
        }
        return allowedHeaders;
    }

    /**
     * 
     * @MethodName: start
     * @Description: 配置类启动
     * @author yuanzhenhui
     * @throws Exception
     * @see io.vertx.core.AbstractVerticle#start()
     * @date 2023-04-13 04:26:30
     */
    @Override
    public void start() throws Exception {
    
    

        // -----------------------------------------------------------------------------------
        // 要使用 @PropLoader 注解必须使用 YamlUtil.propLoadSetter 方法预设,以便通过反射获取类中信息 -
        // -----------------------------------------------------------------------------------
        YamlUtil.propLoadSetter(this);

        // --------------------------------
        // 传入 vertx 对象并创建 Router 实例 -
        // --------------------------------
        Router router = Router.router(vertx);

        // -------------------------------------------------
        // 通过传入 router 实例实现接口绑定和创建当前 http 服务端 -
        // -------------------------------------------------
        createInterface(router);
        createServer(router);
    }

    /**
     * 
     * @MethodName: createInterface
     * @Description: 创建接口
     * @author yuanzhenhui
     * @param router
     *            void
     * @date 2023-04-13 04:26:44
     */
    private void createInterface(Router router) {
    
    
        router.route().handler(BodyHandler.create());

        // ------------
        // 创建跨域设置 -
        // ------------
        CorsHandler cors = CorsHandler.create("*");
        cors.allowedHeaders(setupAllowedHeaders());
        cors.allowedMethods(hmSet);
        router.route().handler(cors);
        router.route().consumes(CommonConstants.HTTP_APPLICATION_JSON);
        router.route().produces(CommonConstants.HTTP_APPLICATION_JSON);

        // ---------------------------------------------------------------------------------------------------------
        // 这里采用了多线程并行处理的方式对接口进行发布绑定处理:
        // 1. 之所以采用 CopyOnWriteArraySet 处理是因为本项目是以功能表来划分实现类的(毕竟 spring 中毒有点深)。
        // 因此表越多实现类也会越多。而每个实现类之间并没有加载的先后顺序关系,因此采用多线程“并行流(parallelStream)”来处理;
        // 2. CommonConstants.ROOT_LEVEL 是项目根目录路径,也就是说 ReflectUtil.getClasses 将从根目录开始递归扫描获取所有类;
        // 3. 通过 interfaces.contains(RouterSet.class) 来判断当前类是否实现 RouterSet 接口;
        // 4. 若是,则通过策略模式对所有实现 router4Restful 方法内容进行发布绑定;
        // ---------------------------------------------------------------------------------------------------------
        CopyOnWriteArraySet<Class<?>> clazzSet = ReflectUtil.getClasses(CommonConstants.ROOT_LEVEL, true);
        clazzSet.parallelStream().forEach(clazz -> {
    
    
            Set<Class<?>> interfaces = new HashSet<>(Arrays.asList(clazz.getInterfaces()));
            if (interfaces.contains(RouterSet.class)) {
    
    
                try {
    
    
                    RouterSet rs = (RouterSet)clazz.newInstance();
                    rs.router4Restful(router);
                } catch (Exception e) {
    
    
                    LOGGER.error("func[RouterConfig.createInterface] Exception [{} - {}] stackTrace[{}] ",
                        new Object[] {
    
    e.getCause(), e.getMessage(), Arrays.deepToString(e.getStackTrace())});
                }
            }
        });
    }

    /**
     * 
     * @MethodName: createServer
     * @Description: 创建服务器接口
     * @author yuanzhenhui
     * @param router
     *            void
     * @date 2023-04-13 04:28:29
     */
    private void createServer(Router router) {
    
    
        
        // ---------------------------------------------------
        // 这里是创建 http server 服务,可自主定义了端口、ssl 等设定
        // ---------------------------------------------------
        vertx.createHttpServer(new HttpServerOptions().setSsl(false)).requestHandler(router::accept).listen(port,
            asyncResult -> {
    
    
                if (asyncResult.failed()) {
    
    
                    LOGGER.error("func[RouterConfig.createServer] Exception [{} - {}]",
                        new Object[] {
    
    asyncResult.cause(), asyncResult.result()});
                } else {
    
    
                    LOGGER.info(" ----- All RESTful interfaces are successfully registered! ----- ");
                }
            });
    }
}

上面说到的一个重点就是通过策略模式进行接口的发布绑定,具体就是这样做的。先定义一个接口,如下图:

public interface RouterSet {
    
    

    /**
     * 
     * @MethodName: router4Restful
     * @Description: restful路由
     * @author yuanzhenhui
     * @param router
     *            void
     * @date 2023-04-13 04:30:55
     */
    public void router4Restful(Router router);

}

然后在接口实现类中进行实现即可,如下图:

public class SysUserBuriedRouter extends AbstractVerticle implements RouterSet {
    
    

    private static final Logger LOGGER = LogManager.getLogger(SysUserBuriedRouter.class);

    // 系统上下文名称
    @PropLoader(key = "server.context")
    private static String context;

    ...

    @Override
    public void start() {
    
    
        YamlUtil.propLoadSetter(this);

        ...
    }

    /**
     * 
     * @MethodName: sendBuriedPointInfo
     * @Description: restful处理类
     * @author yuanzhenhui
     * @param ctx
     *            void
     * @date 2023-04-13 05:02:52
     */
    public void sendBuriedPointInfo(RoutingContext ctx) {
    
    
        ...
    }

    /**
     * 
     * @MethodName: router4Restful
     * @Description: 实现路由转发
     * @author yuanzhenhui
     * @param router
     * @see io.kida.components.routers.RouterSet#router4Restful(io.vertx.ext.web.Router)
     * @date 2023-04-13 05:03:12
     */
    @Override
    public void router4Restful(Router router) {
    
    
        router.post(CommonConstants.HTTP_SLASH + context + CommonConstants.HTTP_SLASH + "sendBuriedPointInfo")
            .handler(this::sendBuriedPointInfo);
    }

}

可以看到 SysUserBuriedRouter 类重写了 RouterSet 接口的 router4Restful 方法,通过策略模式传入的 Router 对象可以直接用于设定接口。router.post 制定了接口的请求方法,而后面的 handler 就是指定了接口的处理类。

通过这种方式就可以实现自动接口扫描和发布,RouterConfig.java 类就不再需要进行编码处理,往后增加或者删除接口都只需要关注对应接口类中的 router4Restful 方法即可。

猜你喜欢

转载自blog.csdn.net/kida_yuan/article/details/131760349