{"id":15138532,"url":"https://github.com/wang007/vertx-start","last_synced_at":"2025-10-23T15:30:25.240Z","repository":{"id":44220618,"uuid":"146989277","full_name":"wang007/vertx-start","owner":"wang007","description":"简单地、快速地启动vert.x的手脚架，保留了vert.x原汁原味的开发方式","archived":false,"fork":false,"pushed_at":"2022-02-11T00:32:44.000Z","size":184,"stargazers_count":103,"open_issues_count":4,"forks_count":20,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-01-30T19:07:53.376Z","etag":null,"topics":["fast","simple","vertx"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wang007.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-09-01T10:30:02.000Z","updated_at":"2025-01-08T07:48:37.000Z","dependencies_parsed_at":"2022-08-29T07:41:44.288Z","dependency_job_id":null,"html_url":"https://github.com/wang007/vertx-start","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wang007%2Fvertx-start","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wang007%2Fvertx-start/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wang007%2Fvertx-start/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wang007%2Fvertx-start/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wang007","download_url":"https://codeload.github.com/wang007/vertx-start/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":237843807,"owners_count":19375207,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fast","simple","vertx"],"created_at":"2024-09-26T07:40:44.771Z","updated_at":"2025-10-23T15:30:24.744Z","avatar_url":"https://github.com/wang007.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# vertx-start\n## 简单地，快速地启动vert.x的手脚架\n#### vertx-start保留了vert.x原汁原味的开发方式，并没有修改运行时的任何东西。\n#### vertx-start非常的轻量级，代码也就那么几行。而且只依赖了vertx-core，vertx-web。  是开发vert.x居家旅行、早日脱单的必备良药。 \n\n实例代码：https://github.com/wang007/vertx-start-example\n\n#### maven坐标\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.github.wang007\u003c/groupId\u003e\n    \u003cartifactId\u003evertx-start\u003c/artifactId\u003e\n    \u003cversion\u003e1.0.1\u003c/version\u003e\n\u003cdependency\u003e\n```\n\n\n### 有什么功能？\n* @Route \n* @Deploy\n* 免copy Json, JsonArray\n* profiles.active\n\n\n### 快速启动vertx-start\n```java\npublic class Main {\n    public static void main(String[] args) {\n\t    Vertx vertx = Vertx.vertx();\n\t    VertxBoot boot = VertxBoot.create(vertx);\n\t    boot.start();\n   }\n}\n```\n 就是这么简单把vertx-start启动起来。 \n这种启动方式确保你的配置文件中有base.paths属性。默认从启动类开始扫描。\n base.paths属性用于指定vertx-start扫描component（组件）的路径。 可以指定多个，用逗号\",\"间隔。\n vertx-start是不管你的vertx怎么获取的，即是说，用Main方式启动也行，也用Launcher也可以。\n \n 当然你也可以在hook方法中把VertxBoot保存出来。\n \n```java\npublic class Main {\n\n    public static void main(String[] args) {\n        Vertx vertx = Vertx.vertx();\n        VertxBoot.create(vertx)\n                .afterDeployedHook(VertxBootHolder::setVertxBoot)\n                .start();\n    }\n    public static final class VertxBootHolder {\n        public volatile static VertxBoot vertxBoot;\n        public synchronized static void  setVertxBoot(VertxBoot boot) {\n            vertxBoot = vertxBoot;\n        }\n        public static VertxBoot getVertxBoot() {\n            return vertxBoot;\n        }\n    }\n}\n``` \n\n#### 痛点1\n大部分情况下，你的Router代码是在Verticle中组织的，当Route的数量少时还好，如果Route数量大的话，所有的Router代码组织到一个Verticle中，这是会让开发者很头痛的事情。\n\u003e 如果Router通过hack方式传到其他的Verticle中，其实这是没用的。Router最终运行所在的eventLoop是httpServer listen的那个Verticle。\n\u003e 这样会有很明显的“意识”问题，以为Router会在其他Verticle的上下文执行，其实不然。\n\n#### vertx-start是如何解决这个痛点的呢？\n\n把Router组织到LoadRouter上，按功能划分不同的功能的Router到LoadRouter中。\n```java\n@Route( mountPath = \"/demo\")\npublic class DemoRouter implements LoadRouter {\n    @Override\n    public void start(Router router, Vertx vertx) {\n        router.route(\"/wang\").handler(rc -\u003e {\n            rc.response().end(\"hello world\");\n        });\n    }\n}\n```\n\n```java\n@Deploy(instances = 16)\npublic class HttpServer extends HttpServerVerticle {}\n```\n\n\n就通过这样简单的几行代码，就可以把httpServer启动起来。 \n而instances是Verticle的实例数，HttpServer对应的Verticle实例数 = eventLoop count，充分发挥Vert.x的性能。\n\n```java\npublic interface LoadRouter {\n\n    /**\n     * {@link LoadRouter}生命周期方法。\n     *\n     * 当且仅当{@link #start(Future)}成功完成， 调用入参中的{@link Future#complete()}\n     *\n     */\n    void start(Future\u003cVoid\u003e future);\n\n    /**\n     *\n     * @return 用于 {@link LoadRouter} 排序， 升序。 默认: 0.\n     */\n    default int order() {\n        return 0 ;\n    }\n\n    /**\n     * {@link LoadRouter}创建好后调用。\n     *\n     * 例如：权限相关的route实现，可以放到该方法中。\n     *\n     * @param router 当使用{@link Route#mountPath()} 挂载路径， router为subRouter, (子路由)\n     * @param server {@link HttpServerVerticle}定义client组件，然后在这里获取，达到所有LoadRouter共享。\n     */\n    default  void init(Router router, Vertx vertx, HttpServerVerticle server) {}\n\n}\n```\n\n* init方法在start方法之前执行。 例如一些前置Route（像权限校验的Route）可以在init方法创建。HttpServerVerticle子类中声明一些client，然后通过server.self()转成对应的子类，获取Client。\n* order方法用于LoadRouter实现类排序，order越小，越前面。意味着越先把LoadRouter中调用Route加到MainRouter容器中。\n* 绝大多数情况下，推荐使用AbstractLoadRouter，需要协程的kt请使用CoroutineRouter。\n\n\n#### @Route怎么使用？\n首先声明一点，@Route只能加到LoadRouter实现类上，否则报错。\n##### @Route中的3个属性。\n* value -\u003e 路径前缀。默认是\"\"，即没有路径前缀。像上面的实例代码， 最终的访问路径是 /demo/wang。即会把value的值拼接到Router定义route的路径中。\n* mountPath -\u003e 挂载路径。默认是\"\"，即不挂载subRouter，直接挂载到MainRouter上。 先声明一点， mountPath跟value不冲突，如果两者同时存在，最终的访问路径是 /mountPath/value/path。\n* sharedMount -\u003e 是否共享挂载subRouter，默认是true。即共享。 绝大多数情况下，都是true。\n\n\u003e\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;如果不熟悉vertx-web的话，会对这个挂载路径有疑问。 \n\u003e\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;Router是Route的容器，里面有skipList保存了所有的Route。如果访问后面的Route的话，需要跟前面的Route逐一匹配。Route数量大的话，对性能有所损失。\n\u003e\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;而mountPath就是Route进行了分类。把一些route加到subRouter上，最后把subRouter加到MainRouter。 更详细的解读，请参考vertx-web官方文档关于mount的解释。\n\u003e\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;mountPath是强烈推荐使用一个属性。\n\n#### 痛点2\n当写完一个Verticle的时候，需要手动调用vertx#deployVerticle方法来部署Verticle。尽管你可能很不情愿，但是你必须这么做。\n```java\n@Deploy\npublic class DemoVerticle extends AbstractVerticle {\n    @Override\n    public void start() throws Exception {\n        vertx.eventBus().\u003cJsonSend\u003econsumer(\"jsonSend\", msg -\u003e {\n            System.out.println(msg.body());\n            msg.reply(new JsonSend().put(\"name\", \"wang007\"));\n        });\n    }\n}\n```\n对，就是这么简单，通过@Deploy注解，就把Verticle部署了。\n\n##### @Deploy的属性\n * instances -\u003e 默认是1。Vertilce的实例数，这个没啥好说的了吧。\n * worker -\u003e 默认是false。是否为workVerticle\n * order -\u003e 默认是0。 部署Verticle时的顺序，值越小，越先部署。假如verticle之间有依赖的话，可以使用该属性。\n \n\u003e 也许你会说，这么属性还不够啊，vertx部署Verticle的时候，有很多属性可选呢， 甚至包括部署完成时的操作。 别急，都有， 听我娓娓道来。 \n\n##### 实现VerticleConfig接口\n```java\n public interface VerticleConfig {\n    /**\n     * 部署verticle的参数\n     * {@link Deploy}中值 != 默认值 将会设置到options中\n     * @return 部署参数\n     */\n    default DeploymentOptions options() {\n        return new DeploymentOptions();\n    }\n    /**\n     * 确保该verticle是单实例的 即{@link DeploymentOptions#instances} = 1\n     * @return true: verticle必须单利，如果{@link Deploy#instances()} != 1 或 {@link #options()}中的instances != null 报错\n     *         false: 允许多利的\n     * @throws IllegalStateException\n     */\n    default boolean requireSingle() {\n        return false ;\n    }\n    /**\n     * 部署verticle完成之后的回调\n     * {@link io.vertx.core.Vertx#deployVerticle(String, Handler)} 中的Handler\n     * @return handler\n     */\n    default  Handler\u003cAsyncResult\u003cString\u003e\u003e deployedHandler() {\n        return null;\n    }\n}\n```\n让verticle实现类实现VerticleConfig接口\n* options方法，设置部署参数。如果@Deploy有设置数，且不等于默认参数。那么会设置到options中。\n* requireSingle方法，确保Verticle单例。默认是false。如果设置返回true。且设置多实例数的话，报错。\n* deployedHandler方法，设置部署完成后操作。默认是null。\n#### HttpServerVerticle\n\u003e 是的，你没看错，要启动一个httpServer，必须继承HttpServerVerticle。并用@Deploy注解。骨灰级推荐**httpServer对应Verticle的实例数等于eventLoop的实例数**。才能充分发挥vert.x的性能。\n\u003e HttpServerVerticle有多个拓展方法。\n\n1. **addressAndPort方法**。默认启动端口：8080，如果8080不合你的胃口。你只需要覆盖该方法，提供你的端口即可。\n2. **可以在init方法初始化一些client，并且initFuture#complete方法通知初始化完成，且public对应的client。然后可以LoadRouter#init方法中获取。\n3. **before方法（敲黑板）**。传入的参数是MainRouter。在执行所有的LoadRouter方法之前执行，可以覆盖该方法，做一些全局的Route操作。 例如BodyHandler等。\n4. doStop方法。传入的参数是httpServer（Vert.x中的）实例，做Verticle stop时的操作。\n5. beforeAccept方法。传入的参数是request。在请求来临时，进入MainRouter之前执行。这一步可以做请求之前拦截操作。\n\n#### 不知道算不算痛点的痛点3\n\u003e \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;熟悉的vert.x的朋友，都知道。eventBus send json，jsonArray的时候，会发生一次copy操作。尽管你的代码中是能确保线程安全的。\n\u003e \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;实现JsonSend，JsonArraySend， 大费周折，最后发现还是不够理想。 这里的不够理想是指send的时候必须要设置codecName。因为我的实现中走不到最后defaultCodecMap中。这个在实现之前没发现。瞎眼程序员。\n\n#### JsonSend，JsonArraySend\n```java\n@Deploy\npublic class DemoVerticle extends AbstractVerticle {\n\n    @Override\n    public void start() throws Exception {\n    \n        vertx.eventBus().\u003cJsonSend\u003econsumer(\"jsonSend\", msg -\u003e {\n            System.out.println(msg.body());\n            msg.reply(new JsonSend().put(\"name\", \"wang007\"));\n        });\n        \n        vertx.eventBus().\u003cJsonArraySend\u003econsumer(\"jsonArraySend\", msg -\u003e {\n            System.out.println(msg.body());\n            msg.reply(new JsonArraySend().add(\"wang007\"));\n        });\n\n        JsonSend json = new JsonSend().put(\"name\", \"wang007\").put(\"hobby\", \"girl\");\n        \n        vertx.eventBus().\u003cJsonSend\u003esend(\"jsonSend\", json, JsonSend.options(), msg -\u003e {\n            System.out.println(msg.result().body());\n        });\n        \n        JsonArraySend array = new JsonArraySend().add(\"xiaowang\");\n        \n        vertx.eventBus().\u003cJsonArraySend\u003esend(\"jsonArraySend\", array, JsonArraySend.options(), msg -\u003e {\n            System.out.println(msg.result().body());\n        });\n    }\n}\n```\n其中JsonSend.options()， JsonArraySend.options()是必须的。 否则还是会发生copy。\n就是说，你可以把JsonSend当成JsonObject来用，JsonArraySend当成JsonArray来用是没问题的。\n#### JsonSend，JsonArraySend是如何实现的。\n1.  \u0026nbsp;\u0026nbsp;其实关于JsonSend，JsonArraySend是一种妥协。\n2.  \u0026nbsp;\u0026nbsp;JsonSend，JsonArraySend维护着一个属性，会自动判断是否调用了eventBus send。如果调用了JsonSend，JsonArraySend就变成不可变。同时这个不可变的json也传到Consumer中， consumer使用这个json也不可变。只能从里面读取数据。\n3.  \u0026nbsp;\u0026nbsp;同时JsonSend，JsonArraySend的使用有一定的限制。例如不能存Map，List，Map可以用JsonObject代替，List可以用JsonArray。还有存进send中的JsonObject，JsonArray将的不可变。切记。尝试存的话，会报错。\n4.  \u0026nbsp;\u0026nbsp;即是说JsonSend，JsonArraySend免copy的实现方式是通过send之后不可变实现的。\njsonSend，JsonArraySend没有100%不可变。但是正常使用是没问题的。还是那句话：你要做傻逼，没人拦得住你。\n\n\n### 属性文件\n\u003e  1. \u0026nbsp;vertx-start默认加载classpath下的application.properties文件。\n\u003e  2. \u0026nbsp;可以调用VertxBoot #setConfigFilePath方法设置classpath下的其他路径\n\u003e  3. \u0026nbsp;如果以上都不满足或者想要添加一些额外的属性， 可以在vertxBoot #start方法之前调用vertxboot #getContainer方法，然后强制成InternalContainer。 再调用appendProperties方法，添加属性。同样，也可以调用appendComponent方法来添加Component到容器中。\n### 关于base.paths\n- \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;  可以添加多个path。\n- \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;  base.paths就是组件的路径。确保注解的Component都包含在base.paths中\n### 关于profiles.active\n- \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;  关于profiles.active，相信使用过spring的朋友都知道，用于指定不同环境的配置文件。\n- \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; 啰嗦一下，profiles.active文件的前缀、后缀必须跟主配置文件一样。\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; 例如：主配置文件：application.properties， profiles.active文件：application-dev.properties\n- \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;profiles.active加载方法且有先后顺序。先去System属性文件中找（通过启动jvm的时候添加-Dbase.paths参数添加）。找不到再去主属性文件中找。找不到就是没有。即不加载profiles.active文件。\n\n- 调用vertxBoot.loadFor方法把属性加到到pojo中。同时pojo使用@Properties注解上。\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwang007%2Fvertx-start","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwang007%2Fvertx-start","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwang007%2Fvertx-start/lists"}