tomcat 我可以从另一个spring boot应用程序内部编译并运行一个spring Boot jar吗?

14ifxucb  于 2022-12-19  发布在  Spring
关注(0)|答案(3)|浏览(166)

bounty将在5天后过期。回答此问题可获得+200声望奖励。Sandeep希望引起更多人对此问题的关注:我希望在这里的样本代码。因为已经检查了过去的答案。并正在寻找一些解决这个问题。

如此快速的澄清,因为我已经读了一些以前类似的问题:
1.我正在寻找编译和运行一个Spring Boot 代码库从一个运行的Spring应用程序。
1.我并不想把多个spring Boot jar嵌套或打包在一个jar中,第二个spring boot代码库在外面,甚至可能在github上。
我已经看过https://www.toptal.com/spring-boot/spring-boot-application-programmatic-launch了,它非常有用,但是我不知道如何编译和加载一个Spring Boot 应用程序。
我有一个暗示,这是在Tomcat Tomcat ServletWebServerFactory级别上完成的-基本上,Spring Boot “助手”应用程序将触发Tomcat加载外部jar并进行部署。

jvidinwx

jvidinwx1#

您不能简单地在从您的主Spring Boot应用程序中启动的 * 外部进程 * 中构建和运行您的辅助Sping Boot 应用程序吗?
我刚刚在一个非常简单的概念验证中尝试了这个方法,为了这个概念验证,我创建了两个虚拟的Sping Boot 应用程序,一个叫做outer,另一个叫做inner,后者应该由前者构建和运行。
以下是目录结构(为简洁起见,省略了两个Gradle项目中的Gradle 7.6 Wrapper文件):

├── inner
│   ├── build.gradle
│   ├── settings.gradle
│   └── src
│       └── main
│           ├── java
│           │   └── com
│           │       └── example
│           │           └── demo
│           │               └── DemoApplication.java
│           └── resources
│               └── application.properties
└── outer
    ├── build.gradle
    ├── settings.gradle
    └── src
        └── main
            └── java
                └── com
                    └── example
                        └── demo
                            └── DemoApplication.java

两个settings.gradle文件都是空的,两个build.gradle文件的内容也是一样的:

plugins {
    id 'java'
    id 'org.springframework.boot' version '2.7.6'
    id 'io.spring.dependency-management' version '1.1.0'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
}

“内部”应用程序是Spring Quickstart Guide的演示应用程序,即inner/src/main/java/com/example/demo/DemoApplication.java如下所示:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @GetMapping("/hello")
    public String hello(@RequestParam(value = "name", defaultValue = "World") String name) {
        return String.format("Hello %s!", name);
    }
}

inner/src/main/resources/application.properties文件还包含server.port=8081,以便其Web服务器运行在与“外部”端口不同的端口上。
这就剩下outer/src/main/java/com/example/demo/DemoApplication.java了,它定义了以下(粗略的)应用程序:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.File;
import java.io.IOException;

@SpringBootApplication
@RestController
public class DemoApplication {

    private Process otherAppProcess = null;

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @PostMapping("/run")
    public String run() throws IOException, InterruptedException {
        synchronized (this) {
            if (otherAppProcess != null) {
                stop();
            }
            var processBuilder = new ProcessBuilder("./gradlew", "bootRun");
            processBuilder.directory(new File("../inner"));
            otherAppProcess = processBuilder.start();
        }
        return "Done.";
    }

    @PostMapping("/stop")
    public String stop() {
        synchronized (this) {
            if (otherAppProcess != null) {
                otherAppProcess.destroy();
                otherAppProcess = null;
            }
        }
        return "Ok.";
    }
}

现在,您可以在outer/中运行./gradlew bootRun,以启动“外部”Sping Boot 应用程序(Tomcat Web服务器)。该服务器会对POST请求做出React,启动“内部”Spring Boot应用程序的Gradle构建,该应用程序也会运行该应用程序(一旦构建完成)。

$ curl -X GET http://localhost:8081/hello
curl: (7) Failed to connect to localhost port 8081 after 0 ms: Connection refused
$ curl -X POST http://localhost:8080/run
Done.
$ curl -X GET http://localhost:8081/hello
Hello World!
$ curl -X POST http://localhost:8080/stop
Ok.
up9lanfz

up9lanfz2#

嗯-我知道你想要的样本代码,但我缺乏时间,目前尝试它由我自己,这可能不是一个真实的的答案-但太大的评论:
我从来没有做过这样的事情,但也许我可以把我的想法在这里-如果它很糟糕,随时忽略它。
我不知道您的“Main Sping Boot Project”是否使用了Maven Wrapper,但让我们假设它使用了。
所以让我们尝试以下概念:

  • 您启动“主”Sping Boot 应用程序,它实际上可以构建和启动多个Spring Boot应用程序。
  • 主Sping Boot 应用程序从GitHub / GitLab checkout X个不同的Spring Boot应用程序(使用git clone)到它选择的一些目录中。(你可以使用JGit或使用**Runtime.getRuntime().exec(“你的git命令”)**或你想到的任何东西来完成)
  • 知道Maven Wrapper存在于此文件夹中后,您基本上可以在target文件夹中构建Sping Boot JAR(或者类似于在Gradle中执行gradlew bootJar
  • 在“Shell命令”成功执行之后,您可以通过执行类似java -jar path/to/your/mySpringBoot.jar fully.qualified.package.Application的命令来启动 Boot

这听起来是不是有点像你想做的?最后,如果我们仔细想想-这是一样的,当你手动 checkout 你的项目,构建JAR并启动它-不是吗?

pxyaymoc

pxyaymoc3#

您可以使用JavaCompiler

public static void main(String[] args) {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
        Iterable<? extends JavaFileObject> fileObjects = fileManager.getJavaFileObjectsFromStrings(Arrays.asList("src/main/java/com/example/app2/Application.java"));
        compiler.getTask(null, fileManager, null, null, null, fileObjects).call();
        fileManager.close();
        
        // you can run jar file from this host application
        String[] newArgs = {"--spring.config.name=externalApp", "--spring.config.additional-location=file:/etc/externalApp/"};
        SpringApplication.run(com.example.externalApp.Application.class, newArgs);
    }

另请参阅文档-〉Java Compiler

相关问题