0

I have a typical Spring Boot REST server:

…

@SpringBootApplication
public class MyService {

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

I include org.springframework.boot:spring-boot-starter-web along with de.flapdoodle.embed:de.flapdoodle.embed.mongo.spring30x in order to embed MongoDB for testing and running in the IDE. This application starts fine from the resulting JAR file.

However I want to disable automatically configuring the embedded MongoDB server; instead I want to enable it based only on certain profiles. My first step is to exclude EmbeddedMongoAutoConfiguration:

…

import de.flapdoodle.embed.mongo.spring.autoconfigure.EmbeddedMongoAutoConfiguration;

@SpringBootApplication(exclude = {EmbeddedMongoAutoConfiguration.class})
public class MyService {

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

So far so good—the application still runs. Now I want to make sure that the embedded MongoDB classes don't get distributed with the application, because I plan to only enable EmbeddedMongoAutoConfiguration in the IDE (based on a profile). So I update my POM like this:

<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <executions>
    <execution>
      <goals>
        <goal>repackage</goal>
      </goals>
      <configuration>
        <excludeGroupIds>de.flapdoodle,de.flapdoodle.embed,de.flapdoodle.graph,
            de.flapdoodle.java8,de.flapdoodle.reverse</excludeGroupIds>
      </configuration>
    </execution>
  </executions>
</plugin>

Suddenly the application no longer starts up, saying:

***************************
APPLICATION FAILED TO START
***************************

Description:

Web application could not be started as there was no org.springframework.boot.web.servlet.server.ServletWebServerFactory bean defined in the context.

Action:

Check your application's dependencies for a supported servlet web server.
Check the configured web application type.

Why does excluding unused class files prevent the application from defining a ServletWebServerFactory?

My guess is that when I indicate @SpringBootApplication(exclude = {EmbeddedMongoAutoConfiguration.class}), Spring will get confused if the EmbeddedMongoAutoConfiguration class is not present. Instead of thinking, "hey, the class doesn't even exist, so I don't have to do anything to exclude it", Spring instead thinks, "I want to exclude this class, but it's not even present, so I don't know what to do!" And instead of producing an error and saying "I can't create the application", it simply doesn't create the necessary beans, and leaves it to later for the application to say, "uh oh, I don't have a ServletWebServerFactory and I have no idea why not".

Does anyone know a way that I can dynamically exclude EmbeddedMongoAutoConfiguration in a way that way that won't confused Spring if EmbeddedMongoAutoConfiguration is in fact not even present in the distribution?

Garret Wilson
  • 18,219
  • 30
  • 144
  • 272
  • 1
    Use a `String` with the classname instead of hardcoding the class like that. Using the string makes it dynamic, hardcoding makes it required. For this use `excludeName` instead of `exclude`. – M. Deinum Apr 18 '23 at 18:27
  • Wonderful! I'll do more testing, but for the moment this seems to have worked! @M.Deinum if you'd like to add this as an answer I can mark it as correct. Does `excludeName` support wildcards by the way? Just curious. – Garret Wilson Apr 18 '23 at 21:13

1 Answers1

2

If you check the javadoc for @SpringBootApplication you will see the exclude and excludeName. The first takes an array of classes the second an array of String. The first, due to classes, will automatically try to load the class as it is part of the bytecode (due to the import), the second will do it lazily.

In your case you want the excludeName instead of exclude. The excludeName takes the full qualified name of the class you want to exclude.

@SpringBootApplication(excludeName = "de.flapdoodle.embed.mongo.spring.autoconfigure.EmbeddedMongoAutoConfiguration"})

Will do the trick.

M. Deinum
  • 115,695
  • 22
  • 220
  • 224
  • This seems to work. Thank you very much! I've updated my blog post [Embedded MongoDB Only in IDE using Spring Boot 3.x](https://www.garretwilson.com/blog/2023/04/02/embedded-mongodb-ide-spring-boot-3) with this information. Much appreciated. – Garret Wilson Apr 19 '23 at 14:18