效果如图

部分源码如下

package top.yunp.jfxdemo

import com.sun.net.httpserver.HttpServer
import javafx.application.Application
import javafx.fxml.FXMLLoader
import javafx.scene.Scene
import javafx.stage.Stage
import java.net.InetSocketAddress
import java.net.URLConnection
import java.util.concurrent.CompletableFuture

fun startStaticServer(): CompletableFuture<Int> {
    val future = CompletableFuture<Int>()

    Thread.ofVirtual().start {
        val server = HttpServer.create(InetSocketAddress("127.0.0.1", 0), 0)
        server.createContext("/") { exchange ->
            val uriPath = exchange.requestURI.path
            val resourcePath = "/static${if (uriPath == "/") "/index.html" else uriPath}"
            val resource = HelloApplication::class.java.getResource(resourcePath)
            if (resource != null) {
                val bytes = resource.readBytes()
                exchange.responseHeaders.add("Content-Type", URLConnection.guessContentTypeFromName(resourcePath))
                exchange.sendResponseHeaders(200, bytes.size.toLong())
                exchange.responseBody.use { it.write(bytes) }
            } else {
                val notFound = "404 Not Found".toByteArray()
                exchange.sendResponseHeaders(404, notFound.size.toLong())
                exchange.responseBody.use { it.write(notFound) }
            }
        }
        server.start()
        future.complete(server.address.port)
    }
    return future
}

class HelloApplication : Application() {
    override fun start(stage: Stage) {

        Globals.internalServerPort = startStaticServer().get()

        val fxmlLoader = FXMLLoader(HelloApplication::class.java.getResource("/jfx/views/hello-view.fxml"))
        val scene = Scene(fxmlLoader.load(), 640.0, 480.0)
        stage.title = "Hello!"
        stage.scene = scene
        stage.show()
    }
}

fun main() {
    Application.launch(HelloApplication::class.java)
}