Durante la última entrada de la serie relacionada con la programación orientada a aspectos analizamos el modo en el que se puede alterar el comportamiento de un sistema, analizando las categorías de advices y realizando una comparativa con los métodos de una clase. A lo largo de esta entrada realizaremos un análisis en profundidad de los advices.
Before advice
Este tipo de advices se ejecutan antes de la ejecución del joint point sobre el que actúan. En el siguiente ejemplo:
Este tipo de advices se ejecutan antes de la ejecución del joint point sobre el que actúan. En el siguiente ejemplo:
before():execution(@Secured * * (..)){
// asegurarse de que el usuario puede realizar la operación
}
el advice realiza una comprobación de seguridad antes de que se produzca la ejecución de cualquier método anotado con Secured.
En caso de que el advice dispare un excepción, el joint point no se ejecutará. Este tipo de advices son comúnmente utilizados en aspectos tales como seguridad o trazabilidad.
After advice
Se ejecutan después de la ejecución del joint point sobre el que actúan.Dentro de esta categoría, AspectJ ofrece tres tipos de advices:
Se ejecutan después de la ejecución del joint point sobre el que actúan.Dentro de esta categoría, AspectJ ofrece tres tipos de advices:
- Ejecución del advice independientemente del resultado de la ejecución del joint point.
- Ejecución del advice única y exclusivamente si el joint point ha finalizado correctamente.
- Ejecución del advice después que el joint point haya disparado una excepción.
Veamos en detalle cada uno de los tres tipos anteriores:
1.- Advice After
Este tipo de advices se ejecutan independientemente del resultado de la ejecución del joint point sobre el que actúan.Habitualmente se conoce a este tipo de advices como after finally puesto que su semántica es similar a la de un bloque finally.
El siguiente advice:
1.- Advice After
Este tipo de advices se ejecutan independientemente del resultado de la ejecución del joint point sobre el que actúan.Habitualmente se conoce a este tipo de advices como after finally puesto que su semántica es similar a la de un bloque finally.
El siguiente advice:
after(): call(@Logging * ServiceManager.*(..)){
registra el resultado de todas las operaciones de la clase ServiceManager que estén marcadas con la anotación Logging, independientemente si retornan correctamente o terminan su ejecución de forma inesperada mediante el disparo de una excepción.
2.-Advice After Returning
En muchas ocasiones,será necesario ejecutar el código de nuestro advice única y exclusivamente cuando la ejecución del joint point haya terminado de forma correcta. Continuando con el ejemplo anterior:
se seguirá registrando el resultado de las operaciones, siempre y cuando, la ejecución haya terminado correctamente, sin el disparo de ninguna excepción.
AspectJ ofrece una pequeña variante para este tipo de advices:
gracias a la cual se permite recuperar el objeto retornado por la ejecución del joint point dentro del advice. Veamos un pequeño ejemplo ilustrativo:
Es importante tener claro que no se puede retornar un objeto nuevo (sí puede ser modificado pero no retornar uno nuevo).
3.- Advice After Exception
Este tipo de advices son similares a los descritos en el apartado anterior. En este caso, el advice se ejecutará única y exclusivamente cuando el joint point dispare una excepción. Presentan la siguiente estructura:
El advice del ejemplo anterior se ejecutará siempre y cuando algún método de la clase ServiceManager (o alguna de sus subclases) dispare una excepción. En el supuesto de que la ejecución del joint point termine correctamente, este tipo de advices no serán ejecutados.
Al igual que los advices del apartado anterior, AspectJ ofrece un modo de recuperar la excepción que ha sido disparada por el joint point de manera que esté disponible en el cuerpo del advice. Siguiendo una sintaxis similar a la anterior, tendríamos:
Un after throwing advice nunca podrá tragarse la excepción, por lo que seguirá subiendo por la pila de llamadas hasta llegar al objeto que realizó la invocación del joint point.
Around advice
Este clase de advices engloban al joint point, pudiendo ejecutar la lógica del mismo un número indefinido de veces. Incluso pueden omitir la ejecución del propio joint point. Algunos de los usos principales de este tipo de advices son los siguientes:
Si desde el around advice se desea llevar a cabo la ejecución del joint point, será necesario hacer uso de la palabra reservada proceed() dentro del cuerpo del advice. Recuérdese que, puesto que la invocación de proceed() ejecuta el joint point, deberán pasarse el mismo número de argumentos que han sido recolectados por el advice.
Asimismo, puesto que la invocación de proceed() supone la ejecución del joint point, el valor de retorno será el retornado por éste último.
A continuación se adjunta un pequeño ejemplo de utilización de advices de este tipo:
Analicemos en detalle la construcción anterior:
// registrar el resultado de la operación
}
registra el resultado de todas las operaciones de la clase ServiceManager que estén marcadas con la anotación Logging, independientemente si retornan correctamente o terminan su ejecución de forma inesperada mediante el disparo de una excepción.
2.-Advice After Returning
En muchas ocasiones,será necesario ejecutar el código de nuestro advice única y exclusivamente cuando la ejecución del joint point haya terminado de forma correcta. Continuando con el ejemplo anterior:
after () returning: call(@Logging * ServiceManager.*(..)){
// registrar el resultado de la operación
}
AspectJ ofrece una pequeña variante para este tipo de advices:
after() returning (ReturnType returnObject)
gracias a la cual se permite recuperar el objeto retornado por la ejecución del joint point dentro del advice. Veamos un pequeño ejemplo ilustrativo:
after() returning (java.sql.Connection connection):
call(java.sql.Connection DriverManager.getConnection(..)){
System.out.println("Se ha recuperado la conexión "
+ connection);
}
Es importante tener claro que no se puede retornar un objeto nuevo (sí puede ser modificado pero no retornar uno nuevo).
3.- Advice After Exception
Este tipo de advices son similares a los descritos en el apartado anterior. En este caso, el advice se ejecutará única y exclusivamente cuando el joint point dispare una excepción. Presentan la siguiente estructura:
after() throwing:execution (* ServiceManager+.*(..))
El advice del ejemplo anterior se ejecutará siempre y cuando algún método de la clase ServiceManager (o alguna de sus subclases) dispare una excepción. En el supuesto de que la ejecución del joint point termine correctamente, este tipo de advices no serán ejecutados.
Al igual que los advices del apartado anterior, AspectJ ofrece un modo de recuperar la excepción que ha sido disparada por el joint point de manera que esté disponible en el cuerpo del advice. Siguiendo una sintaxis similar a la anterior, tendríamos:
after() throwing (ExceptionType exceptionObject):
Un after throwing advice nunca podrá tragarse la excepción, por lo que seguirá subiendo por la pila de llamadas hasta llegar al objeto que realizó la invocación del joint point.
Around advice
Este clase de advices engloban al joint point, pudiendo ejecutar la lógica del mismo un número indefinido de veces. Incluso pueden omitir la ejecución del propio joint point. Algunos de los usos principales de este tipo de advices son los siguientes:
- Ejecución de lógica adicional antes y después de la ejecución de un joint point, como por ejemplo, acciones de profiling.
- Omitir la ejecución original, y realizar otra en su lugar, como por ejemplo, operaciones con cachés.
- Envolver la operación con el objetivo de aplicar una política de gestión de excepciones. Un ejemplo de este uso sería la gestión de transacciones.
Si desde el around advice se desea llevar a cabo la ejecución del joint point, será necesario hacer uso de la palabra reservada proceed() dentro del cuerpo del advice. Recuérdese que, puesto que la invocación de proceed() ejecuta el joint point, deberán pasarse el mismo número de argumentos que han sido recolectados por el advice.
Asimismo, puesto que la invocación de proceed() supone la ejecución del joint point, el valor de retorno será el retornado por éste último.
A continuación se adjunta un pequeño ejemplo de utilización de advices de este tipo:
void around(User user,int credits)
throws InsufficientCreditsException:
call(* User.pay*(int)) && target(user) & & args(credits){
try
{
proceed(user,credits);
}catch(InsufficientCreditsException ex){
if(!processException()){
throw ex;
}
}
- El pointcut selecciona cualquier llamada a los métodos de la clase User cuyo nombre comience por pay y disparen una excepción de tipo InsufficientCreditsException.
- La segunda parte del pointcut recolecta el contexto del joint point: el usuario sobre el que se está realizando la llamada y el número de créditosque se están pasando como argumento del método que se está ejecutando.
- En el cuerpo del advice, se engloba la ejecución del método con un bloque de gestión de excepciones, para realizar una protección adicional en caso de que se produzca una excepción. En el caso de que la protección adicional no sea correcta, la excepción será disparada de nuevo.
Todos los around advices deben declarar un valor de retorno (pudiendo ser void). Habitualmente el tipo de retorno de éstos se corresponde con el tipo de retorno de los joint points sobre los que está actuando.
En algunas ocasiones, todos los joint points sobre los que actúa el advice no presentan el mismo tipo de retorno, como puede ocurrir cuando estamos añadiendo soporte transaccional a diferentes operaciones. En estas situaciones el tipo de retorno que debe declarar el advice será Object. AspectJ acomodará el valor de retorno de acuerdo a las siguientes reglas:
En algunas ocasiones, todos los joint points sobre los que actúa el advice no presentan el mismo tipo de retorno, como puede ocurrir cuando estamos añadiendo soporte transaccional a diferentes operaciones. En estas situaciones el tipo de retorno que debe declarar el advice será Object. AspectJ acomodará el valor de retorno de acuerdo a las siguientes reglas:
- Si se está retornando un tipo primitivo, AspectJ realizará el boxing/unboxing correspondiente. Esta característica es similar a la incluida a partir de Java 5, pero AspectJ no precisa de dicha versión de Java para realizar la operación.
- En el caso en el que el tipo de retorno no sea primitivo, AspectJ realizará los casts oportunos antes de retornar el valor.
- Objetos (incluyendo los tipos primitivos) que conforman el joint point.
- Anotaciones asociadas al joint point.
Pointcut | Contexto recuperado |
---|---|
this(obj) | Objeto this en el joint point que se está ejecutando |
target(obj) | Objetivo de la llamada en el joint point que se está ejecutando. En el caso de un joint point de una llamada a un método, el target será el objeto que realiza la llamada. Para la ejecución de un método, el target será el objeto this. En los accesos a campos, el target será el objeto que se está accediendo. En el resto de joint points no existe un target disponible |
args(obj1,obj2,...) | Objetos que representa los argumentos en el joint point. Para las llamadas/ejecuciones de métodos/constructores, recupera los argumentos de los mismos. En el caso de los manejadores de excepciones, recupera la excepción producida. Para los accesos en modo escritura a un campo, recupera el nuevo valor del campo. |
@this(annot) | Anotación asociada con el tipo del objeto this del joint point |
@target(annot) | Anotación asociada con el tipo del objeto target del joint point |
@args(annot1, annot2, ...) | Anotación asociada con el tipo de los argumentos del joint point |
@within(annot) | Anotación asociada con el tipo "enclosing" del joint point |
@withincode(annot) | Anotación asociada con el método "enclosing" del joint point |
annotation(annot) | Anotación asociada con el asunto actual del joint point. |
Durante la próxima entrada analizaremos el concepto de Aspecto.
Hasta pronto!
No hay comentarios:
Publicar un comentario