Introducción
En el capítulo anterior se explicó cómo gestionar objetos bean mediante configuración XML. Ahora abordaremos la gestión del contenedor Spring y los objetos bean utilizando anotaciones personalizadas.
Definición de anotaciones personalizadas
Anotación @Inyeccion
package org.miproyecto.anotacion;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Inyeccion {
boolean esRequerido() default true;
}
Anotación @ComponenteDAO
package org.miproyecto.anotacion;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Component;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ComponenteDAO {
@AliasFor(annotation = Component.class)
String valor() default "";
}
Anotación @ComponenteServicio
package org.miproyecto.anotacion;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Component;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ComponenteServicio {
@AliasFor(annotation = Component.class)
String valor() default "";
}
Implementación de la capa de negocio
Interfza de servicio
package org.miproyecto.servicio;
public interface ServicioUsuario {
void crearUsuario();
}
Implementación del servicio
package org.miproyecto.servicio.impl;
import org.miproyecto.anotacion.Inyeccion;
import org.miproyecto.anotacion.ComponenteServicio;
import org.miproyecto.dao.DaoUsuario;
import org.miproyecto.servicio.ServicioUsuario;
@ComponenteServicio
public class ImplementacionServicioUsuario implements ServicioUsuario {
@Inyeccion
private DaoUsuario daoUsuario;
@Override
public void crearUsuario() {
System.out.println("ImplementacionServicioUsuario - ejecutando");
daoUsuario.crearUsuario();
}
}
Interfaz DAO
package org.miproyecto.dao;
public interface DaoUsuario {
void crearUsuario();
}
Implementación DAO
package org.miproyecto.dao.impl;
import org.miproyecto.anotacion.ComponenteDAO;
import org.miproyecto.dao.DaoUsuario;
@ComponenteDAO
public class ImplementacionDaoUsuario implements DaoUsuario {
@Override
public void crearUsuario() {
System.out.println("ImplementacionDaoUsuario - ejecutando");
}
}
Clase principal del contenedor
Esta clase es responsable de escanear el paquete base, detectar clases anotadas y registraras como beans, además de realizar la inyección de dependencias.
package org.miproyecto.contexto;
import org.miproyecto.anotacion.Inyeccion;
import org.miproyecto.anotacion.ComponenteDAO;
import org.miproyecto.anotacion.ComponenteServicio;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ContextoAnotaciones {
private String paqueteBase;
private Map<Class, Object> fabricaBeans = new ConcurrentHashMap<>();
private String rutaRaiz = null;
public ContextoAnotaciones(String paqueteBase) throws Exception {
this.paqueteBase = paqueteBase;
escanearArchivos();
inyectarDependencias();
}
private void escanearArchivos() throws Exception {
String rutaPaquete = this.paqueteBase.replaceAll("\\.", "\\\\");
Enumeration<URL> recursos = Thread.currentThread()
.getContextClassLoader()
.getResources(rutaPaquete);
while (recursos.hasMoreElements()) {
URL url = recursos.nextElement();
File directorio = new File(URLDecoder.decode(url.getFile(), "UTF-8"));
if (rutaRaiz == null) {
rutaRaiz = directorio.getAbsolutePath()
.substring(0, directorio.getAbsolutePath().indexOf(rutaPaquete));
}
cargarBeans(directorio);
}
}
private void cargarBeans(File directorio) throws Exception {
if (directorio.isDirectory()) {
File[] archivos = directorio.listFiles();
if (archivos == null || archivos.length == 0) {
return;
}
for (File archivo : archivos) {
if (archivo.isDirectory()) {
cargarBeans(archivo);
} else if (archivo.isFile()) {
procesarArchivo(archivo);
}
}
}
}
private void procesarArchivo(File archivo) throws IOException,
ClassNotFoundException, NoSuchMethodException,
InvocationTargetException, InstantiationException,
IllegalAccessException {
String nombreCompleto = archivo.getPath().substring(rutaRaiz.length());
if (nombreCompleto.contains(".class")) {
nombreCompleto = nombreCompleto.replaceAll("\\\\", "\\.")
.substring(0, nombreCompleto.length() - 6);
Class<?> clase = Class.forName(nombreCompleto);
if (!clazz.isInterface()) {
ComponenteServicio anotacionServicio = clase.getAnnotation(ComponenteServicio.class);
ComponenteDAO anotacionDAO = clase.getAnnotation(ComponenteDAO.class);
if (anotacionServicio != null || anotacionDAO != null) {
Object instancia = clase.getDeclaredConstructor().newInstance();
if (clase.getInterfaces().length > 0) {
Class interfaz = clase.getInterfaces()[0];
fabricaBeans.put(interfaz, instancia);
} else {
fabricaBeans.put(clase, instancia);
}
}
}
}
}
private void inyectarDependencias() throws IllegalAccessException {
Iterator<Map.Entry<Class, Object>> iterator = fabricaBeans.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Class, Object> entrada = iterator.next();
Object instancia = entrada.getValue();
Class clase = instancia.getClass();
Field[] campos = clase.getDeclaredFields();
for (Field campo : campos) {
Inyeccion inyeccion = campo.getAnnotation(Inyeccion.class);
if (inyeccion != null) {
campo.setAccessible(true);
Object dependencia = fabricaBeans.get(campo.getType());
campo.set(instancia, dependencia);
}
}
}
}
public Object obtenerBean(Class clase) {
return fabricaBeans.get(clase);
}
}
Prrueba del sistema
package org.miproyecto;
import org.miproyecto.contexto.ContextoAnotaciones;
import org.miproyecto.servicio.ServicioUsuario;
public class AplicacionPrincipal {
public static void main(String[] args) {
System.out.println("Iniciando aplicación...");
ejecutarPruebaAnotaciones();
}
public static void ejecutarPruebaAnotaciones() {
try {
ContextoAnotaciones contexto = new ContextoAnotaciones("org.miproyecto");
ServicioUsuario servicio = (ServicioUsuario) contexto.obtenerBean(ServicioUsuario.class);
servicio.crearUsuario();
} catch (Exception excepcion) {
throw new RuntimeException(excepcion);
}
}
}
Resultado de la ejecución
Iniciando aplicación...
ImplementacionServicioUsuario - ejecutando
ImplementacionDaoUsuario - ejecutando