Resolución del Error "Missing ServletWebServerFactory" en Spring Boot con IDEA

Este error, comúnmente conocido como missing ServletWebServerFactory bean, suele manifestarse en entornos de desarrollo como IntelliJ IDEA y puede tener diversas causas, a menudo relacionadas con configuraciones incorrectas en el proyecto.

Diagnóstico del Problema

Al iniciar un proyecto Spring Boot en IDEA, puede aparecer un error similar al siguiente:

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2019-06-18 16:52:29.850 ERROR 9248 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:157) ~[spring-boot-2.1.5.RELEASE.jar:2.1.5.RELEASE]
	...
Caused by: org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.getWebServerFactory(ServletWebServerApplicationContext.java:206) ~[spring-boot-2.1.5.RELEASE.jar:2.1.5.RELEASE]
	...

El mensaje central, Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean, indica que Spring Boot no puede encontrar una instancia de ServletWebServerFactory, que es esencial para iniciar el servidor web embebido (como Tomcat, Jetty o Undertow).

Investigación y Solución Inicial

Analizando el código fuente de Spring Boot, la clase ServletWebServerApplicationContext busca un bean de tipo ServletWebServerFactory. Si no encuentra ninguno, lanza la excepción mencionada. Las implementaciones comunes de esta interfaz son TomcatServletWebServerFactory, JettyServletWebServerFactory y UndertowServletWebServerFactory.

Una primera aproximación podría ser definir manualmente el bean en la clase principal de la aplicación:

import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration // O en la clase principal de la aplicación
public class AppConfig {

    @Bean
    public ServletWebServerFactory servletWebServerFactory() {
        return new TomcatServletWebServerFactory();
    }
}

Sin embargo, ejecutar el proyecto después de este cambio puede resultar en un error diferente, aunque relacionado:

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.web.servlet.server.ServletWebServerFactory]: Factory method 'servletWebServerFactory' threw exception; nested exception is java.lang.NoClassDefFoundError: org/apache/catalina/core/AprLifecycleListener
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
	...
Caused by: java.lang.ClassNotFoundException: org.apache.catalina.core.AprLifecycleListener
	at java.net.URLClassLoader.findClass(URLClassLoader.java:382) ~[na:1.8.0_191]
	...

Este nuevo error, java.lang.ClassNotFoundException: org.apache.catalina.core.AprLifecycleListener, señala la ausencia de una clase específica del servidor Tomcat.

Causa Raíz y Solución Definitiva

La causa subyacente de la ClassNotFoundException a menudo se relaciona con la conifguración del scope de la dependenica de spring-boot-starter-tomcat en el archivo pom.xml (para proyectos Maven):


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <scope>provided</scope> <!-- Problema potencial -->
</dependency>

Cuando el scope se establece en provided, la dependencia de Tomcat se excluye del paquete de la aplicación final, asumiendo que estará disponible en el entorno de ejecución (por ejemplo, un servidor de aplicaciones externo). Sin embargo, en un entorno de desarrollo con IDEA, esta configuración puede impedir que las clases de Tomcat sean cargadas correctamente durante la fase de inicio de la aplicación, llevando a la ClassNotFoundException.

La solución consiste en modificar el scope de la dependencia a compile (que es el valor por defecto y no necesita ser especificado explícitamente si no se desea otro comportamiento):


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <!-- scope>compile<!-- O simplemente omitir la línea scope -->
</dependency>

Al cambiar el scope a compile, la dependencia de Tomcat se incluirá en el classpath de la aplicación durante el desarrollo, permitiendo que las clases necesarias se carguen y el servidor web se inicie correctamente. Es importante recordar revertir el scope a provided si se desea un empaquetado específico para despliegue en contenedores que ya proveen Tomcat, o dejarlo como compile si la aplicación debe ser autocontenida.

Tras este ajuste, el proyecto debería iniciarse sin errores relacionados con el ServletWebServerFactory.

Etiquetas: spring-boot intellij-idea servletwebserverfactory tomcat maven

Publicado el 5-29 15:26