public void execute() throws MojoExecutionException, MojoFailureException {
if (this.skip) {
getLog().debug("skipping run as per configuration.");
return;
}
run(getStartClass());
}
private String getStartClass() throws MojoExecutionException {
String mainClass = this.mainClass;
if (mainClass == null) {
try {
mainClass = MainClassFinder.findSingleMainClass(this.classesDirectory,
SPRING_BOOT_APPLICATION_CLASS_NAME);
}
catch (IOException ex) {
throw new MojoExecutionException(ex.getMessage(), ex);
}
}
if (mainClass == null) {
throw new MojoExecutionException("Unable to find a suitable main class, please add a 'mainClass' property");
}
return mainClass;
}
这里涉及到到mainClass的寻找,参考:spring-boot-maven-plugin:repackage分析-优快云博客,我们来看下run方法:
private void run(String startClassName) throws MojoExecutionException, MojoFailureException {
boolean fork = isFork();
this.project.getProperties().setProperty("_spring.boot.fork.enabled", Boolean.toString(fork));
if (fork) {
doRunWithForkedJvm(startClassName);
}
else {
logDisabledFork();
runWithMavenJvm(startClassName, resolveApplicationArguments().asArray());
}
}
protected void logDisabledFork() {
if (getLog().isWarnEnabled()) {
if (hasAgent()) {
getLog().warn("Fork mode disabled, ignoring agent");
}
if (hasJvmArgs()) {
RunArguments runArguments = resolveJvmArguments();
getLog().warn("Fork mode disabled, ignoring JVM argument(s) ["
+ String.join(" ", runArguments.asArray()) + "]");
}
if (hasWorkingDirectorySet()) {
getLog().warn("Fork mode disabled, ignoring working directory configuration");
}
}
}
先看doRunWithForkedJvm,fork模式启用,允许个性化定制参数
private void doRunWithForkedJvm(String startClassName) throws MojoExecutionException, MojoFailureException {
List<String> args = new ArrayList<>();
addAgents(args);
addJvmArgs(args);
addClasspath(args);
args.add(startClassName);
addArgs(args);
runWithForkedJvm((this.workingDirectory != null) ? this.workingDirectory : this.project.getBasedir(), args,
determineEnvironmentVariables());
}
private void addAgents(List<String> args) {
File[] configuredAgents = determineAgents();
if (configuredAgents != null) {
if (getLog().isInfoEnabled()) {
getLog().info("Attaching agents: " + Arrays.asList(configuredAgents));
}
for (File agent : configuredAgents) {
args.add("-javaagent:" + agent);
}
}
if (this.noverify) {
args.add("-noverify");
}
}
private File[] determineAgents() {
return (this.agents != null) ? this.agents : this.agent;
}
private void addJvmArgs(List<String> args) {
RunArguments jvmArguments = resolveJvmArguments();
Collections.addAll(args, jvmArguments.asArray());
logArguments("JVM argument(s): ", jvmArguments.asArray());
}
protected RunArguments resolveJvmArguments() {
StringBuilder stringBuilder = new StringBuilder();
if (this.systemPropertyVariables != null) {
stringBuilder.append(this.systemPropertyVariables.entrySet().stream()
.map((e) -> SystemPropertyFormatter.format(e.getKey(), e.getValue()))
.collect(Collectors.joining(" ")));
}
if (this.jvmArguments != null) {
stringBuilder.append(" ").append(this.jvmArguments);
}
return new RunArguments(stringBuilder.toString());
}
static class SystemPropertyFormatter {
static String format(String key, String value) {
if (key == null) {
return "";
}
if (value == null || value.isEmpty()) {
return String.format("-D%s", key);
}
return String.format("-D%s=\"%s\"", key, value);
}
}
private void addClasspath(List<String> args) throws MojoExecutionException {
try {
StringBuilder classpath = new StringBuilder();
for (URL ele : getClassPathUrls()) {
if (classpath.length() > 0) {
classpath.append(File.pathSeparator);
}
classpath.append(new File(ele.toURI()));
}
if (getLog().isDebugEnabled()) {
getLog().debug("Classpath for forked process: " + classpath);
}
args.add("-cp");//指示classPath路径
args.add(classpath.toString());
}
catch (Exception ex) {
throw new MojoExecutionException("Could not build classpath", ex);
}
}
private void addArgs(List<String> args) {
RunArguments applicationArguments = resolveApplicationArguments();
Collections.addAll(args, applicationArguments.asArray());
logArguments("Application argument(s): ", applicationArguments.asArray());
}
protected RunArguments resolveApplicationArguments() {
RunArguments runArguments = (this.arguments != null) ? new RunArguments(this.arguments)
: new RunArguments(this.commandlineArguments);
addActiveProfileArgument(runArguments);
return runArguments;
}
private void addActiveProfileArgument(RunArguments arguments) {
if (this.profiles.length > 0) {
StringBuilder arg = new StringBuilder("--spring.profiles.active=");
for (int i = 0; i < this.profiles.length; i++) {
arg.append(this.profiles[i]);
if (i < this.profiles.length - 1) {
arg.append(",");
}
}
arguments.getArgs().addFirst(arg.toString());
logArguments("Active profile(s): ", this.profiles);
}
}
private Map<String, String> determineEnvironmentVariables() {
EnvVariables envVariables = resolveEnvVariables();
logArguments("Environment variable(s): ", envVariables.asArray());
return envVariables.asMap();
}
protected EnvVariables resolveEnvVariables() {
return new EnvVariables(this.environmentVariables);
}
可以看出这整个过程都是拼凑java参数,不多说,我们来看真正的执行方法:runWithForkedJvm
protected abstract void runWithForkedJvm(File workingDirectory, List<String> args,
Map<String, String> environmentVariables) throws MojoExecutionException, MojoFailureException;
@Override
protected void runWithForkedJvm(File workingDirectory, List<String> args, Map<String, String> environmentVariables)
throws MojoExecutionException {
int exitCode = forkJvm(workingDirectory, args, environmentVariables);
if (exitCode == 0 || exitCode == EXIT_CODE_SIGINT) {
return;
}
throw new MojoExecutionException("Application finished with exit code: " + exitCode);
}
private int forkJvm(File workingDirectory, List<String> args, Map<String, String> environmentVariables)
throws MojoExecutionException {
try {
RunProcess runProcess = new RunProcess(workingDirectory, new JavaExecutable().toString());
Runtime.getRuntime().addShutdownHook(new Thread(new RunProcessKiller(runProcess)));
return runProcess.run(true, args, environmentVariables);
}
catch (Exception ex) {
throw new MojoExecutionException("Could not exec java", ex);
}
}
可以看出执行的是RunProcess#run:
public int run(boolean waitForProcess, Collection<String> args, Map<String, String> environmentVariables)
throws IOException {
ProcessBuilder builder = new ProcessBuilder(this.command);
builder.directory(this.workingDirectory);
builder.command().addAll(args);
builder.environment().putAll(environmentVariables);
builder.redirectErrorStream(true);
builder.inheritIO();
try {
Process process = builder.start();
this.process = process;
SignalUtils.attachSignalHandler(this::handleSigInt);
if (waitForProcess) {
try {
return process.waitFor();
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
return 1;
}
}
return 5;
}
finally {
if (waitForProcess) {
this.endTime = System.currentTimeMillis();
this.process = null;
}
}
}
这个方法可以参考复用,还有JavaExecutable也可以复用
runWithMavenJvm(startClassName, resolveApplicationArguments().asArray());来看第二个分支
protected abstract void runWithMavenJvm(String startClassName, String... arguments)
throws MojoExecutionException, MojoFailureException;
protected void runWithMavenJvm(String startClassName, String... arguments) throws MojoExecutionException {
IsolatedThreadGroup threadGroup = new IsolatedThreadGroup(startClassName);
//LaunchRunner用于main的反射执行
Thread launchThread = new Thread(threadGroup, new LaunchRunner(startClassName, arguments), "main");
launchThread.setContextClassLoader(new URLClassLoader(getClassPathUrls()));
launchThread.start();
join(threadGroup);
threadGroup.rethrowUncaughtException();
}
private void join(ThreadGroup threadGroup) {
boolean hasNonDaemonThreads;
//阻塞 等待所有线程执行完毕
do {
hasNonDaemonThreads = false;
Thread[] threads = new Thread[threadGroup.activeCount()];
//将活跃的线程加入到
threadGroup.enumerate(threads);
for (Thread thread : threads) {
if (thread != null && !thread.isDaemon()) {
try {
hasNonDaemonThreads = true;
thread.join();
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
}
while (hasNonDaemonThreads);
}
这里从maven切出一个线程来执行应用程序,而fork模式则是采用客户端java拼装java命令来执行