Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vert.x中不能使用Kotlin重写Launcher类 #3

Open
z-950 opened this issue Sep 7, 2020 · 0 comments
Open

Vert.x中不能使用Kotlin重写Launcher类 #3

z-950 opened this issue Sep 7, 2020 · 0 comments
Labels

Comments

@z-950
Copy link
Owner

z-950 commented Sep 7, 2020

环境:openJDK 13、Kotlin 1.3.72、Vert.x 3.9.x

遇到需要重写Launcher进行固定配置的情况,参考官方例子

由于个人主要使用Kotlin进行编写,所以重写Launcher类自然也会用Kotlin。下面介绍遇到的问题。

main函数的编写

对于Kotlin,main可以单独定义,也可以使用伴生对象在类里面定义。

Launcher类是启动类,所以Vert.x的Launcher类中定义了main方法。重写时,也需要定义自己main方法。

自己的Launcher写在MyLauncher.kt中。

使用伴生对象的方式定义main函数

class MyLauncher : io.vertx.core.Launcher() {
    companion object {
        @JvmStatic
        fun main(args: Array<String>) {
            MyLauncher().dispatch(args)
        }
    }
}

上面的写法会报错:Accidental override: The following declarations have the same JVM signature (main([Ljava/lang/String;)V)

经过搜索,了解到可以通过@JvmName注解改变重写的方法的名字来解决该错误,但此为main函数,不可修改名字,故无解。

单独定义main函数

fun main(args: Array<String>) {
    MyLauncher().dispatch(args)
}

class MyLauncher : io.vertx.core.Launcher() {}

不会报错。来看一下能否运行。

配置build.gradle的mainClass为自己的Launcher:vertx { launcher = "com.example.starter.MyLauncher" }

此处使用了io.vertx.vertx-plugin这个插件。只用java插件时,应该配置mainClassName字段

编译并运行。报错:java.lang.ClassNotFoundException: com.example.starter.Launcher

看一下build/classes里面有什么:

+-- com.example.starter
    +-- MyLauncherKt.class
    +-- MyLauncher.class

具体进去看一下,发现main方法放到了MyLauncher.class里。修改一下mainClass的位置:vertx { launcher = "com.example.starter.MyLauncherKt" }。再次编译运行,没有报错。

单独定义main函数可行吗

答案是不可行,标题已经说明了。

至于为什么不可行,还是出于方便角度考虑的。

mainVerticle设置

对于Vert.x,一个jar里还得包括mainVerticle用于启动,mainVerticle中才是应用的启动逻辑。

mainVerticle可以在build.gradle里设置,放到manifest里。这样我们可以直接运行jar,否则还得传入mainVerticle参数。

首先说明,传入mainVerticle的麻烦之处(对于微服务架构):

  1. 使用ide进行调试时,每个微服务启动的task都需要再传入mainVerticle参数
  2. mainVerticle名字修改后,需要手动修改传入的参数

手动配置参数是容易忘记的,毫无意义,不如将其自动化方便。

mainVerticle如何被获取

dispatch所在的类io.vertx.core.impl.launcher.VertxCommandLauncher中,有如下函数:

  private String getFromManifest(String key) {
    try {
      Enumeration<URL> resources = RunCommand.class.getClassLoader().getResources("META-INF/MANIFEST.MF");
      while (resources.hasMoreElements()) {
        try (InputStream stream = resources.nextElement().openStream()) {
          Manifest manifest = new Manifest(stream);
          Attributes attributes = manifest.getMainAttributes();
          String mainClass = attributes.getValue("Main-Class");
          if (main.getClass().getName().equals(mainClass)) {
            String value = attributes.getValue(key);
            if (value != null) {
              return value;
            }
          }
        }
      }
    } catch (IOException e) {
      throw new IllegalStateException(e.getMessage());
    }
    return null;
  }

其中attributes.getValue("Main-Class")获取到的是build.gradle中配置的mainClass,也就是MyLauncherKt

main.getClass().getName()获取到的则是运行时的Launcher的class,也就是MyLauncher

所以,单独定义main函数,并且配置正确的情况下,Vert.x的Launcher是无法获取到manifest中的mainVerticle的。

改变main函数编译后的文件名

目前找不到此做法。

如果使用@file:JvmName("MyLauncher")强制输出为MyLauncher.class,则会报错:Duplicate JVM class name 'com/example/starter/MyLauncher' generated from: package-fragment com.example.starter, MyLauncher

修改为@file:JvmName("Launcher")可以解决此错误,但Launcher.class里只有main函数,并且MyLauncher类依然在MyLauncher.class里,相当于没用。

总结

综上,解决办法是,使用Java重写Launcher和其中的main函数。

看到有资料说main函数无法被重写(override),实践了一下,其实是可以的。

@z-950 z-950 added the 后端 label Sep 7, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant