[Java] Beginner Vert.x (3)

5. Routing configuration + interface publishing

In the previous section, we also mentioned that we use recursive traversal to deploy all the inheritance implementations of the AbstractVerticle class to the service, as shown in the following figure:

...

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();

...

And our routing configuration class also inherits from AbstractVerticle, so it will be automatically deployed, as shown in the following figure:

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! ----- ");
                }
            });
    }
}

One of the key points mentioned above is to publish and bind the interface through the strategy mode, which is exactly how it is done. First define an interface, as shown below:

public interface RouterSet {
    
    

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

}

Then implement it in the interface implementation class, as shown in the figure below:

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);
    }

}

It can be seen that the SysUserBuriedRouter class rewrites the router4Restful method of the RouterSet interface, and the Router object passed in through the strategy mode can be directly used to set the interface. router.post specifies the request method of the interface, and the subsequent handler is the processing class that specifies the interface.

In this way, automatic interface scanning and publishing can be realized, and the RouterConfig.java class does not need to be coded. Adding or deleting interfaces in the future only needs to pay attention to the router4Restful method in the corresponding interface class.

Guess you like

Origin blog.csdn.net/kida_yuan/article/details/131760349