Spring Framework tiene integración incorporada para usar Spring MVC con cualquier biblioteca de plantillas que pueda ejecutarse según el motor de secuencias de comandos de especificación JSR-223 en Java. Probamos las siguientes bibliotecas de plantillas en varios motores de ejecución de scripts:

La regla básica para integrar cualquier otro motor de script es que debe implementar las interfaces ScriptEngine e Invocable.

Requisitos

Debe proporcionar un motor de ejecución de script en su classpath, cuyos detalles dependen del motor de ejecución de script:

  • Un motor de procesamiento de JavaScript llamado Nashorn se entrega con Java 8+. Se recomienda encarecidamente que utilice la última actualización disponible.

  • JRuby debe agregarse como una dependencia para admitir el lenguaje Ruby.

  • Jython debe agregarse como una dependencia para proporcionar compatibilidad con el lenguaje Python.

  • Para brindar soporte para scripts Kotlin, debe agregar la dependencia org.jetbrains.kotlin:kotlin-script-util y el archivo META-INF/services/javax.script.ScriptEngineFactory, que contiene la cadena org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngineFactory. Para obtener más información, consulte este ejemplo.

Necesita tener una biblioteca de plantillas de script. Una forma de hacer esto para JavaScript es WebJars.

Plantillas de guiones

Puede declarar un bean ScriptTemplateConfigurer para especificar qué motor de script usar, qué archivos de script cargar, qué función llamar para representar plantillas, etc. El siguiente ejemplo utiliza plantillas de Moustache y un motor JavaScript llamado Nashorn:

Java
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.scriptTemplate();
    }
    @Bean
    public ScriptTemplateConfigurer configurer() {
        ScriptTemplateConfigurer configurer = new ScriptTemplateConfigurer();
        configurer.setEngineName("nashorn");
        configurer.setScripts("mustache.js");
        configurer.setRenderObject("Mustache");
        configurer.setRenderFunction("render");
        return configurer;
    }
}
Kotlin
@Configuration
@EnableWebMvc
class WebConfig : WebMvcConfigurer {
    override fun configureViewResolvers(registry: ViewResolverRegistry) {
        registry.scriptTemplate()
    }
    @Bean
    fun configurer() = ScriptTemplateConfigurer().apply {
        engineName = "nashorn"
        setScripts("mustache.js")
        renderObject = "Mustache"
        renderFunction = "render"
    }
}

El siguiente ejemplo muestra la misma forma de organizar en XML:

<mvc:annotation-driven/>
<mvc:view-resolvers>
    <mvc:script-template/>
</mvc:view-resolvers>
<mvc:script-template-configurer engine-name="nashorn" render-object="Mustache" render-function="render">
    <mvc:script location="mustache.js"/>
</mvc:script-template-configurer>

El controlador será el mismo para las configuraciones Java y XML, como se muestra en el siguiente ejemplo:

Java
@Controller
public class SampleController {
    @GetMapping("/sample")
    public String test(Model model) {
        model.addAttribute("title", "Sample title");
        model.addAttribute("body", "Sample body");
        return "template";
    }
}
Kotlin
@Controller
class SampleController {
    @GetMapping("/sample")
    fun test(model: Model): String {
        model["title"] = "Sample title"
        model["body"] = "Sample body"
        return "template"
    }
}

El siguiente ejemplo muestra la plantilla Moustache:

<html>
    <head>
        <title>{{title}}</title>
    </head>
    <body>
        <p>{{body}}</p>
    </body>
</html>

La función de visualización se llama con los siguientes parámetros:

  • String template: contenido de la plantilla

  • Map model: Ver modelo

  • RenderingContext renderContext: RenderingContext, que proporciona acceso al contexto de la aplicación, la configuración regional, el cargador de plantillas y la URL (desde la versión 5.0).

Mustache.render() es compatible de forma nativa con esta firma, por lo que puedes llamarla directamente.

Si su tecnología de plantillas requiere cierta personalización, puede pasar un script que implemente una función de visualización personalizada. Por ejemplo, Handlerbars requiere que se compilen las plantillas antes de usarlas y requiere polyfill para emular algunas capacidades del navegador que no están disponibles en el motor de secuencias de comandos del lado del servidor.

El siguiente ejemplo muestra cómo hacer esto:

Java
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.scriptTemplate();
    }
    @Bean
    public ScriptTemplateConfigurer configurer() {
        ScriptTemplateConfigurer configurer = new ScriptTemplateConfigurer();
        configurer.setEngineName("nashorn");
        configurer.setScripts("polyfill.js", "handlebars.js", "render.js");
        configurer.setRenderFunction("render");
        configurer.setSharedEngine(false);
        return configurer;
    }
}
Kotlin
@Configuration
@EnableWebMvc
class WebConfig : WebMvcConfigurer {
    override fun configureViewResolvers(registry: ViewResolverRegistry) {
        registry.scriptTemplate()
    }
    @Bean
    fun configurer() = ScriptTemplateConfigurer().apply {
        engineName = "nashorn"
        setScripts("polyfill.js", "handlebars.js", "render.js")
        renderFunction = "render"
        isSharedEngine = false
    }
}
Es necesario establecer la propiedad sharedEngine en false cuando se utilizan motores de ejecución de scripts no seguros para subprocesos con bibliotecas de plantillas que no se calculan para el paralelismo, por ejemplo, Manubrios o React ejecutándose en Nashorn. En este caso, se requiere la actualización 60 de Java SE 8 debido a este error, pero En general, se recomienda utilizar la última versión del parche Java SE de todos modos.

polyfill.js define solo el objeto window que Manillar necesita para funcionar correctamente, de la siguiente manera:

var ventana = {};

Esta implementación básica de render.js compila la plantilla antes de usarla. Una implementación lista para producción también debe almacenar cualquier plantilla en caché reutilizable o plantilla precompilada. Esto se puede hacer en el lado del script (y manejar cualquier personalización que necesite, por ejemplo, administrar la configuración del motor de plantillas). El siguiente ejemplo muestra cómo hacer esto:

function render(template, model) {
    var compiledTemplate = Handlebars.compile(template);
    return compiledTemplate(model);
}

Consulte las pruebas unitarias de Spring Framework, Java y resources para ver más ejemplos de configuración.