I wrote a spring application with multiple main classes. When I try to run it as follows:
mvn spring-boot:run \
-Dspring-boot.run.profiles=profile2 \
-Dspring-boot.run.main-class=com.example.demo.DemoApplication2
I get this error:
Execution default-cli of goal org.springframework.boot:spring-boot-maven-plugin:3.1.5:run failed: Unable to find a single main class from the following candidates [com.example.demo.DemoApplication2, com.example.demo.DemoApplication]
If I remove the following from my pom.xml:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.5</version>
<relativePath/>
</parent>
then it works! seems like a bug.
Comment From: scottfrederick
Thanks for getting in touch. Unfortunately, you have not provided enough information for us to be able to help you. If you would like us to spend some time investigating, please provide a complete minimal sample that reproduces the problem. You can share it with us by pushing it to a separate repository on GitHub or by zipping it and attaching it to this issue.
Comment From: codespearhead
The correct flag is -Dstart-class, not -Dspring-boot.run.main-class.
Minimal Reproducible Example
- Create a demo Spring Boot project:
mkdir demo &&
cd demo &&
curl https://start.spring.io/starter.tgz -d type=maven-project | tar -xzvf -
- Delete the main class
rm src/main/java/com/example/demo/DemoApplication.java
- Create two main classes (
DemoApplication1.javaandDemoApplication2.java):
cat > src/main/java/com/example/demo/DemoApplication1.java <<EOF && cat > src/main/java/com/example/demo/DemoApplication2.java <<EOF
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication1 {
public static void main(String[] args) {
SpringApplication.run(DemoApplication1.class, args);
}
}
EOF
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication2 {
public static void main(String[] args) {
SpringApplication.run(DemoApplication2.class, args);
}
}
EOF
-
Run the project:
-
Error path:
./mvnw spring-boot:run
# Or: ./mvnw spring-boot:run -Dspring-boot.run.main-class=com.example.demo.DemoApplication1
# Or: ./mvnw spring-boot:run -Dspring-boot.run.main-class=com.example.demo.DemoApplication2
- Happy path:
./mvnw spring-boot:run -Dstart-class=com.example.demo.DemoApplication1
# Or: ./mvnw spring-boot:run -Dstart-class=com.example.demo.DemoApplication2
Comment From: siddhsql
attached MRE as zipped file issue40145.zip
If the correct flag is -Dstart-class then couple of questions wait to be begged:
-
Why is it not documented in official documentation? Official documentation says to use
-Dspring-boot.run.main-class[1] -
Why does the
-Dspring-boot.run.main-classflag work when I don't use following inpom.xml:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.4</version>
<relativePath/>
</parent>
Comment From: codespearhead
Upon closer inspection, you're right: you shouldn't have to resort to a JVM system property (start-class) when there's already an allegedly setting for development convenience (spring-boot.run.main-class).
However, here's what Bael Dung has to say:
A Spring Boot application’s main class is a class that contains a public static void main() method that starts up the Spring ApplicationContext. By default, if the main class isn’t explicitly specified, Spring will search for one in the classpath at compile time and fail to start if none or multiple of them are found. [...] Spring Boot expects the artifact’s Main-Class metadata property to be set to org.springframework.boot.loader.JarLauncher (or WarLauncher) which means that passing our main class directly to the java command line won’t start our Spring Boot application correctly.
Hence, I stand corrected: this is a bug in my opinion.
Comment From: philwebb
This is an interesting problem and I'm not sure how to best solve it. Our spring-boot-starter-parent POM configures a few plugins (including spring-boot-maven-plugin) to use a ${start-class} variable. It seems that even if that variable isn't set, the act of configuring the plugin in the XML disables support for the spring-boot.run.main-class property.
We'll need to discuss this some more to work out what we should do.
Comment From: siddhsql
It is surprising (and embarrassing - no offence) that a basic thing such as this is broken and there was no bug report on it.
Comment From: codespearhead
That's the beauty of open source: you get feedback that you otherwise probably wouldn't.
This bug hasn't been caught for so long because you're structuring your project in a non-conventional multi-module way.
That isn't to say that your your is inheritedly wrong: it simply flew under the radar because few have chosen this approach and thus, it’s less likely that many encountered this bug and felt compelled to report it.
Comment From: mhalbritter
If we add this in our parent:
<properties>
<spring-boot.run.main-class>${start-class}</spring-boot.run.main-class>
</properties>
and then change the config of the spring-boot-maven-plugin in the parent:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>${spring-boot.run.main-class}</mainClass>
</configuration>
</plugin>
then both start-class and spring-boot.run.main-class would work.
Comment From: philwebb
The other plugins we configure with ${start-class} are:
- maven-jar-plugin
- maven-war-plugin
- maven-shade-plugin
I don't think any of those have -D properties that the user can set. Given that, I think we should try the approach @mhalbritter suggests above. Since it's a little risky, and there is a work-around, we'll do this in 3.3.x.