Tu primera Annotation chispas (II)

Segunda parte de mi serie dedicada a las Annotations.

Ya teniamos nuestra annotation @Foo creada, y la estabamos declarando en el VO. Ahora vamos a intentar usar de alguna manera esos metodos que habiamos dejado anotados.

Pongamos que por ejemplo queremos hacer un toString del VO, pero solo queremos que se nos pinten las propiedades anotadas, y sus atributos.


package test.absurdo;

import test.absurdo.vo.AbsurdoVO;

public class TestAbsurdo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		AbsurdoVO absurdo=new AbsurdoVO();
		absurdo.setBar0(3);
		System.out.println(absurdo);
		System.out.println("fin");
	}
}

La ejecucion de ese codigo evidentemente nos sacara una basura parecida a esta:


test.absurdo.vo.AbsurdoVO@82ba41
fin

Por eso vamos a modificar ligeramente la cabecera de nuestro VO:


package test.absurdo.vo;

import test.absurdo.annotation.Anotado;
import test.absurdo.annotation.chorranotacion.Foo;

public class AbsurdoVO extends Anotado{

[.....]

Y en Anotado vamos a sobreescribir el metodo toString. El codigo es bastante sencillo de entender sabiendo poquito de Reflection pero aun asi, voy a ponerle un poco de literatura…

Basicamente, marco el metodo toString con la annotation @Override, lo cual no es mandatorio, pero no esta de mas.
En #1 recopilo todos los metodos del VO.(getMethods())
En #2, con un foreach de java5, recorro todos y cada uno de estos metodos.
En #3, recupero la annotation de clase Foo del metodo en cuestion, y en #4 y #5 descarto aquellos metodos que no tienen la annotation @Foo.
Las lineas #6 y #7 las he puesto para simplificar el ejemplo; si el metodo tiene parametros tambien lo descarto, solo tendre en cuenta los metodos que esten anotados con @Foo en los getters. Por tanto, por simplicidad, si hubiesemos puesto @Foo en un setter, no lo tendremos en cuenta.

Quizas el quid de todo este en #8, donde añado a un LikedList de Strings lo que quiero que mas tarde sea la salida del toString, y esto es, en mi caso, annotation.alias() que me devuelve el valor del atributo alias de la annotation @Foo. Si @Foo tuviese un atributo llamado patata, la llamada seria annotation.patata(). No es necesaria ninguna comprobacion para recuperar el atributo alias, ya que tiene un valor por defecto, asi que siempre tiene un valor, pero si no tuviese un valor por defecto, y ademas hubiesemos usado la annotation en algun lugar sin darle ningun valor a alias, entonces saltaria un error en tiempo de compilacion. Por tanto, no es necesario comprobar si la llamada annotation.alias() devuelve null o no. Ademas, en #8, tambien llamo al getter m.invoke(this) que me devolvera el valor de la propiedad. Como ya me he asegurado de que el metodo no tiene parametros, no necesito tampoco comprobaciones adicionales aqui. Ademas, le paso solo un parametro, porque como en java 5, invoke esta definido con var args Object java.lang.reflect.Method.invoke (Object obj, Object… args), una caracteristica de los var args, es que no necesito poner ningun parametro, si todos los argumentos variables son null.

Ya despues de esto solo nos queda recorrer la lista de String y preparar la salida de nuestro toString.


package test.absurdo.annotation;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;

import test.absurdo.annotation.chorranotacion.Foo;

public class Anotado {
	@Override
	public String toString(){
		String output = "";
		List descriptores = new LinkedList();
		Method[] metodos = this.getClass().getMethods();//#1
		for (Method m: metodos){//#2
			Foo annotation = m.getAnnotation(Foo.class);//#3
			if (annotation == null) {//#4
				continue;//#5
			}
			if (m.getParameterTypes().length>0){//#6
				//Por simplicidad,
				//@Foo solo se considera en los getters
				//Pasamos de setters y demas...
				continue;//#7
			}
			try {
				descriptores.add(annotation.alias() +
				" "+m.invoke(this));//#8
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {

				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		}
		// si no hay metodos anotados con @Foo,
		//usar el toString por defecto
		if (descriptores.isEmpty()) {
			output=super.toString();
		}else{
			for (String d : descriptores){
				output=output+ d+ ";";
			}
		}
		return output;
	}
}

Y ya esta! Tachan! La salida, de la ejecucion de nuestro main ahora sera mucho mas potita:


Bar de tapas 3;Bar de copas 2;Bar que te clavan 5;Foola 8;
fin

Con esto ya deberiamos ser capaces de hacer y usar nuestras propias annotations.

Se pueden usar las annotations sin tener que extender una clase que haga uso de la annotation? Evidentemente si. Todos los dias usamos annotations de Java5, o Hibernate 3.0, en nuestros VOs, sin tener que hacer ningun extends especifico.

Lo dejo para una tercera parte…

Comentarios

Comments are closed.