Crear una aplicación web rápidamente usando Spring Boot

Estoy trabajando en un proyecto nuevo en el cual me dieron carta blanca para poder escoger con que trabajar. Lo que tuve bien en claro es que quería empezar a usar Java 8 y como ya tengo algo de experiencia con Spring decidi también usar este framework.

Si ustedes ya tienen experiencia usando Spring, saben que para usar Spring MVC hay que configurar muchas cosas antes de poder ver al menos una página web sencilla. Entre todo esto hay que configurar el deployment descriptor (i.e. el archivo web.xml); o si estan usando Java EE (Evil Edition?) con un contenedor que soporte el spec de Servlet 3.0+, igual necesitan configurar un WebApplicationInitializer.

Pero vamos, aunque hoy en día es posible configurar Spring sin usar una sola línea de XML sigue siendo mucho código por escribir solo para empezar (y ni siquiera he tocado el configurar otros frameworks como Hibernate, Thymeleaf, JSF, etc.).

¿Una configuracion "Inteligente" o un Code Golf?

Como les encanta decir a muchos, code is king, así que vamos a ver solo dos archivos con código: una configuración de gradle para construir el proyecto y una sola clase de Java para crear nuestro siempre querido "Hola, Mundo!" usando Spring MVC.

Primero, crea un nuevo directorio y dentro de el, crea el archivo 'build.gradle' con el siguiente contenido:

apply plugin: 'java'
apply plugin: 'spring-boot'

sourceCompatibility = 1.8
version = '0.0.1-SNAPSHOT'

dependencies {
   
    compile 'org.springframework.boot:spring-boot-starter-web'
    compile 'org.slf4j:slf4j-api:1.7.+'

    testCompile 'junit:junit:4.11'
    testCompile 'org.mockito:mockito-all:1.9.+'
    testCompile 'org.assertj:assertj-core:1.6.+'
    testCompile 'ch.qos.logback:logback-classic:1.1.+'

    testCompile 'org.springframework.boot:spring-boot-starter-test'
}

repositories {
    mavenCentral()
}

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.1.5.RELEASE")
    }
}

Y nuestra clase Java, la tienes que crear en el directorio 'src/main/java/nombre/paquete/'

package org.javamexico.blogs.mael.spring.boot;

import ...;

@RestController
@EnableAutoConfiguration
public class Main {

    @RequestMapping("/")
    String home() {
        return "Hello World!";
    }

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

¡Y eso fue todo! El truco aquí es tener gradle instalado y ejectuar el siguiente comando:

gradle bootRun

Nota: ¿Cómo instalo gradle? Ve ahora mismo y mejor instala GVM Tool... y con ella instala Gradle.

Y veras algo como esto (junto a muchos logs de como va levantando tu aplicación):

:bootRun

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '
_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.1.8.RELEASE)

Ve a tu explorador e introduce la siguiente dirección:

http://localhost:8080/

Y voilà, ya tienes tu hola mundo usando Spring MVC (con Spring Boot), con menos de 60 líneas de código (o algo así).

Tras bambalinas

Para que este ejemplo funcionara fue necesario que se agregara el plugin de Spring Boot para Gradle y la dependencia spring-boot-starter-web. El plugin entre muchas cosas agrega el task bootRun el cual ejecutara el método main de la clase que escribimos; además evitara que tengamos que especificar la versión de ciertas dependencias "blessed" (¿Con la bendición de Spring?) e.g. no especificamos la versión del artefacto starter-web.

Con respecto a la dependencia 'starter-web', esta contiene mas dependencias transitivas hacia artefactos necesarios para crear una aplicación web usando Spring MVC; mas Tomcat como contenedor.

@EnableAutoConfiguration, @Conditional, y mucha magia

Ahora que sabes como es que Spring Boot trae las dependencias necesarias, creo que aun te preguntas como fue que Spring dedujo que es lo que habia que configurar. Esto fue usando la anotacion @EnableAutoConfiguration. Una vez que Spring Boot detecta esta anotación, empezara a escanear el classpath buscando clases anotadas como componentes y configuraciones i.e. usando la anotación @Component y sus respectivas especializaciones ademas de la anotaciones @AutoConfiguration y @Configuration.

Una vez que encuentra estos componentes, inspeccionara sus contenidos, encontrando varias anotaciones que empiezan con @Conditional (e.g. @ConditionalOnClass, @ConditionalOnMissingBean) y dependiendo de si existen ciertas clases o beans respectivamente, empezaran a registrar beans en el ApplicationContext.

Para ilustrar esto, es mejor que veamos algo de código:

@Order(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
@Import(EmbeddedServletContainerCustomizerBeanPostProcessorRegistrar.class)
public class EmbeddedServletContainerAutoConfiguration {

    /**
     * Nested configuration for if Tomcat is being used.
     */

    @Configuration
    @ConditionalOnClass({ Servlet.class, Tomcat.class })
    @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
    public static class EmbeddedTomcat {

        @Bean
        public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
            return new TomcatEmbeddedServletContainerFactory();
        }

    }
    // mas cosas aqui
}

Si inspeccionan esta clase, verán que el intento aquí es que si en el classpath tenemos las clases Servlet y Tomcat; y si además no tenemos un bean definido de tipo EmbeddedServletContainerFactory; entonces creamos este bean y lo ponemos a disposición del ApplicationContext (pero solo si es una aplicación web, por eso el @ConditionalOnWebApplication).

Esto es lo que hace posible que tengamos Tomcat en nuestra aplicación sin siquiera tener que configurar todo lo que implica iniciarle como contenedor integrado y observamos que tiene defaults sensibles.

El código lo pueden encontrar en Github, en este repositorio.