Search code examples
spring-bootembedded-mongodb

No Spring ServletWebServerFactory when excluding embedded MongoDB classes


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?


Solution

  • 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.