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