Desacoplando Controladores en un Framework MVC JavaWeb Personalizado

Eliminando acoplamiento en la configuración de controladores

La implementación inicial de nuestro framework MVC requería modificar manualmente el código al añadir nuevos controladores. Para solucionarlo, trasladaremos la lista de controladores a un archivo de configuración XML.

Dependencias necesarias

Utilizaremos dom4j para el análisis de archivos XML, aunque se puede emplear cualquier biblioteca similar.

Implementación de la solución

Paso 1: Configuración en web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
                        http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">    
    <listener>
        <listener-class>com.mvc.listener.UrlMappingRegistry</listener-class>
    </listener>    
    
    <custom-mvc>
        <controllers>
            <controller>com.mvc.controller.SaludoController</controller>
        </controllers>
    </custom-mvc>
    
    <servlet>
        <servlet-name>main</servlet-name>
        <servlet-class>com.mvc.servlet.CentralServlet</servlet-class>        
    </servlet>
    <servlet-mapping>
        <servlet-name>main</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

Paso 2: Lectura dinámica de configuración

package com.mvc.listener;

import org.dom4j.*;
import org.dom4j.io.SAXReader;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.io.File;
import java.lang.reflect.Method;
import java.util.*;

public class UrlMappingRegistry implements ServletContextListener {
    private static List<ControladorMVC> mappings;
    
    private final String MVC_TAG = "custom-mvc";
    private final String CTRL_LIST_TAG = "controllers";
    private final String CTRL_TAG = "controller";

    private List<String> obtenerControladores(ServletContextEvent ctx) throws DocumentException {
        List<String> controladores = new ArrayList<>();
        String rutaWebXml = ctx.getServletContext().getRealPath("WEB-INF/web.xml");
        Document doc = new SAXReader().read(new File(rutaWebXml));
        Element raiz = doc.getRootElement();
        
        List<Element> nodosCtrl = raiz.element(MVC_TAG)
                                     .element(CTRL_LIST_TAG)
                                     .elements(CTRL_TAG);
                                     
        for (Element nodo : nodosCtrl) {
            controladores.add(nodo.getText());
        }
        return controladores;
    }

    @Override
    public void contextInitialized(ServletContextEvent ctx) {
        mappings = new ArrayList<>();
        try {
            for (String nombreClase : obtenerControladores(ctx)) {
                Class<?> clazz = Class.forName(nombreClase);
                String urlClase = "";
                
                if (clazz.isAnnotationPresent(MapeoURL.class)) {
                    urlClase = clazz.getAnnotation(MapeoURL.class).url();
                }
                
                for (Method metodo : clazz.getMethods()) {
                    if (metodo.isAnnotationPresent(MapeoURL.class)) {
                        ControladorMVC mapping = new ControladorMVC();
                        mapping.setUrl(urlClase + metodo.getAnnotation(MapeoURL.class).url());
                        mapping.setClaseControlador(nombreClase);
                        mapping.setMetodo(metodo.getName());
                        mappings.add(mapping);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static List<ControladorMVC> getMappings() {
        return mappings;
    }
}

Validación del funcionamiento

Nuevo controlador de ejemplo

package com.mvc.controller;

import com.mvc.annotation.MapeoURL;

@MapeoURL(url="/Comida")
public class ComidaController {
    
    @MapeoURL(url="/Manzana")
    public String comerManzana() {
        System.out.println("Comiendo manzanas");
        return "Manzana";
    }
    
    @MapeoURL(url="/Banana")
    public String comerBanana() {
        System.out.println("Comiendo bananas");
        return "Banana";
    }
}

Actualización de web.xml

<custom-mvc>
    <controllers>
        <controller>com.mvc.controller.SaludoController</controller>
        <controller>com.mvc.controller.ComidaController</controller>
    </controllers>
</custom-mvc>

Al iniciar el servidor, las siguientes URLs deben funcionar correctamente:
http://localhost:8080/MiMVC/Comida/Manzana
http://localhost:8080/MiMVC/Comida/Banana
http://localhost:8080/MiMVC/Saludo/Hola

Etiquetas: java Servlets MVC DOM4J ConfiguraciónXML

Publicado el 7-4 18:38