Ciclos de vida de los consejos
Cada aviso es un Spring Bean. Una instancia de un consejo puede ser común a todos los objetos equipados con consejos o única para cada objeto equipado con consejos. Esto corresponde a consejos por clase o por instancia.
El consejo más utilizado es por clase. Esto es adecuado para asesoramiento general, como asesores transaccionales. No dependen del estado del objeto proxy y no agregan un nuevo estado. Simplemente funcionan de acuerdo con el método y los argumentos.
Las sugerencias para cada instancia son adecuadas para introducciones y para admitir mixins. En este caso, el consejo agrega estado al objeto proxy.
Puede utilizar una combinación de consejos generales y por instancia en un único proxy AOP.
Tipos de consejos en Spring
Spring proporciona varios tipos de sugerencias y se puede ampliar para admitir tipos de sugerencias personalizados. Esta sección describe los conceptos básicos y los tipos estándar de consejos.
Consejo de intercepción alrededor
El tipo de consejo más fundamental en Spring es el consejo de intercepción alrededor.
Spring es compatible con la
interfaz del proyecto AOP Alliance
para trabajar con sugerencias que utilizan la intercepción de
llamadas a métodos. Las clases que implementan MethodInterceptor
e implementan el consejo "interceptar"
también deben implementar la siguiente interfaz:
public interface MethodInterceptor extends Interceptor {
Object invoke(MethodInvocation invocation) throws Throwable;
}
El argumento MethodInvocation
del método invoke()
abre el método llamado, la
conexión objetivo, el proxy AOP y los argumentos del método. El método invoke()
debe devolver el
resultado de la llamada: el valor de retorno del punto de conexión.
El siguiente ejemplo muestra una
implementación de muestra de MethodInterceptor
:
public class DebugInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("Before: invocation=[" + invocation + "]");
Object rval = invocation.proceed();
System.out.println("Invocation returned");
return rval;
}
}
class DebugInterceptor : MethodInterceptor {
override fun invoke(invocation: MethodInvocation): Any {
println ("Before: invocation=[$invocation]")
val rval = invocation.proceed()
println("Invocation returned")
return rval
}
}
Tenga en cuenta la llamada al método proceed()
desde MethodInvocation
. Sigue una
cadena de interceptores hasta el punto de conexión. La mayoría de los interceptores llaman a este método y devuelven
su valor de retorno. Sin embargo, MethodInterceptor
, como cualquier otro consejo, puede devolver un
valor diferente o generar una excepción en lugar de llamar al método de continuación. Pero no debería hacer esto sin
una buena razón.
MethodInterceptor
las implementaciones proporcionan
compatibilidad con otras implementaciones de AOP Alliance que cumplen con los requisitos del AOP. Los otros tipos de
consejos discutidos en el resto de esta sección implementan conceptos generales de AOP, pero de una manera
específica de Spring. Si bien existe una ventaja en utilizar el tipo de consejo más específico, siga el consejo de
MethodInterceptor
si necesita implementar un aspecto en otro marco de AOP. Tenga en cuenta que
actualmente los sectores no son funcionalmente compatibles entre marcos y la Alianza AOP no puede definir interfaces
de sectores.
Consejo previo
Un tipo más simple de consejo es el consejo "antes" (antes )". No requiere un objeto
MethodInvocation
porque se llama solo antes de ingresar el método.
La principal ventaja del
consejo "antes" es que no es necesario llamar al proceder()
método y, por lo tanto, no hay manera de
dejar de continuar inadvertidamente con la cadena interceptora.
La siguiente lista muestra la interfaz MethodBeforeAdvice
:
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method m, Object[] args, Object target) throws Throwable;
}
(La estructura de la API de Spring permite un campo antes del consejo, aunque la interceptación de campos es manejada por objetos normales, por lo que es poco probable que Spring alguna vez lo haga. implementar esto).
Tenga en cuenta que el tipo de retorno es void
. El consejo "antes" es capaz de inyectar
lógica personalizada antes de que se ejecute el punto de unión, pero no puede cambiar el valor de retorno. Si el
consejo "antes" genera una excepción, se detendrá la ejecución adicional de la cadena del interceptor. La excepción
se propagará hacia arriba en la cadena de interceptores. Si no está marcado o está en la firma del método llamado,
se pasará directamente al código del cliente. De lo contrario, el proxy AOP lo incluirá en una excepción no
verificada.
El siguiente ejemplo muestra el consejo "antes" en Spring que cuenta todas las llamadas a métodos:
public class CountingBeforeAdvice implements MethodBeforeAdvice {
private int count;
public void before(Method m, Object[] args, Object target) throws Throwable {
++count;
} public int getCount() {
return count;
}
}
class CountingBeforeAdvice : MethodBeforeAdvice {
var count: Int = 0
override fun before(m: Method, args: Array<Any>, target: Any?) {
++count
}
}
La sugerencia "lanzar excepción"
La sugerencia "lanzar excepción (lanzar)" se llama después del punto de
unión se devuelve si el punto de unión ha generado una excepción. Spring introduce un consejo escrito para "lanzar
una excepción". Tenga en cuenta que esto significa que la interfaz org.springframework.aop.ThrowsAdvice
no contiene ningún método. Esta es una etiqueta de interfaz que identifica que este objeto implementa uno o más
métodos escritos del consejo "lanzar una excepción". Deben tener el siguiente formato:
afterThrowing([Method, args, target], subclassOfThrowable)
Solo se requiere el último argumento. Las firmas de métodos pueden tener uno o cuatro argumentos, dependiendo de si el método de asesoramiento está interesado en el método y los argumentos. Los dos listados siguientes muestran clases que son ejemplos de consejos para "lanzar excepciones".
Si se lanza una excepción RemoteException
(incluso desde subclases), se llama al siguiente consejo:
public class RemoteThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(RemoteException ex) throws Throwable {
// Do something with the remote exception
}
}
class RemoteThrowsAdvice : ThrowsAdvice {
fun afterThrowing(ex: RemoteException) {
// Do something with the remote exception
}
}
A diferencia del consejo anterior, el siguiente ejemplo declara cuatro argumentos, por lo que el
consejo tiene acceso al método que se llama, los argumentos del método y el objeto de destino. Cuando ocurre una
ServletException
, se llama al siguiente consejo:
public class ServletThrowsAdviceWithArguments implements ThrowsAdvice {
public void afterThrowing(Method m, Object[] args , Object target, ServletException ex) {
// Do something with all the arguments
}
}
class ServletThrowsAdviceWithArguments : ThrowsAdvice {
fun afterThrowing(m: Method, args: Array<Any>, target: Any, ex: ServletException) {
// Do something with all the arguments
}
}
El último ejemplo ilustra cómo estos dos métodos se pueden usar en una sola clase que maneja tanto RemoteException
como ServletException
. En una clase puede combinar cualquier número de métodos del consejo de
"lanzar una excepción". La siguiente lista muestra el ejemplo más reciente:
public static class CombinedThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(RemoteException ex) throws Throwable {
// Do something with remote exception
}
public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {
// Do something with all the arguments
}
}
class CombinedThrowsAdvice : ThrowsAdvice {
fun afterThrowing(ex: RemoteException) {
// Do something with the remote exception
}
fun afterThrowing(m: Method, args: Array<Any> , target: Any, ex: ServletException) {
// Do something with all the arguments
}
}
Consejos de devolución
El consejo "después de regresar" en Spring debe implementar
org.springframework.aop.AfterReturningAdvice
interfaz, como se muestra en el siguiente listado:
public interface AfterReturningAdvice extends Advice {
void afterReturning(Object returnValue, Method m, Object[] args, Object target)
throws Throwable;
}
El aviso posterior al retorno tiene acceso al valor de retorno (que no puede cambiar), el método llamado, los argumentos del método y el objetivo.
El siguiente consejo "después del retorno" cuenta todas las llamadas exitosas a métodos que no generaron una excepción:
public class CountingAfterReturningAdvice implements AfterReturningAdvice {
private int count;
public void afterReturning(Object returnValue, Method m, Object[] args, Object target)
throws Throwable {
++count;
}
public int getCount() {
return count;
}
}
class CountingAfterReturningAdvice : AfterReturningAdvice {
var count: Int = 0
private set
override fun afterReturning( returnValue: Any?, m: Method, args: Array<Any>, target: Any?) {
++count
}
}
Este consejo no cambiar la ruta de ejecución. Si arroja una excepción, se pasa a lo largo de la cadena del interceptor en lugar del valor de retorno.
Consejos de introducción
Spring trata el consejo de "Introducción" como un tipo especial de consejo de "intercepción".
La introducción requiere IntroductionAdvisor
y
IntroducciónInterceptor
, que implementan la siguiente interfaz:
public interface IntroductionInterceptor extends MethodInterceptor {
boolean implementsInterface(Class intf);
}
El método invoke()
, heredado de la interfaz MethodInterceptor
de AOP Alliance, debe
implementarse la introducción. Por lo tanto, si el método llamado está en la interfaz inyectada, el interceptor
de
inyección es responsable de manejar la llamada al método; no puede llamar a proceed()
.
La
sugerencia "introducir" no puede usarse con cualquier segmento, ya que solo se aplica a nivel de clase, no a
nivel
de método. Sólo puedes utilizar el consejo de "introducción" con IntroductionAdvisor
, que tiene los
siguientes métodos:
public interface IntroductionAdvisor extends Advisor, IntroductionInfo {
ClassFilter getClassFilter();
void validateInterfaces() throws IllegalArgumentException;
}
public interface IntroductionInfo {
Class<?>[] getInterfaces();
}
No hay ningún MethodMatcher
y, por lo tanto, ningún Pointcut
asociado con el
consejo de "introducción". Sólo el filtrado de clases es lógico.
El método getInterfaces()
devuelve las interfaces representadas por este EA.
El método validateInterfaces()
El método se
utiliza para comprobar si las interfaces inyectadas pueden implementarse mediante el
IntroductionInterceptor
configurado.
Considere un ejemplo del conjunto de pruebas Spring y suponga que queremos inyectar la siguiente interfaz en uno o más objetos:
public interface Lockable {
void lock();
void unlock();
boolean locked();
}
interface Lockable {
fun lock()
fun unlock()
fun locked(): Boolean
}
Este ejemplo ilustra un mixin. Necesitamos poder convertir objetos recomendados a Lockable
,
independientemente de su tipo, y llamar a métodos de bloqueo y desbloqueo. Si llamamos al método
lock()
,
queremos que todos los configuradores lancen una excepción LockedException
. Entonces podemos
agregar un
aspecto que brinda la capacidad de hacer que los objetos sean inmutables sin que ellos lo sepan: un buen ejemplo
de
AOP.
Primero, necesitamos un IntroductionInterceptor
que haga todo el trabajo duro. En este
caso, ampliamos la clase auxiliar org.springframework.aop.support.DelegatingIntroductionInterceptor
.
Sería posible implementar IntroductionInterceptor
directamente, pero usar DelegatingIntroductionInterceptor
es más adecuado para la mayoría de los casos.
DelegatingIntroductionInterceptor
está previsto
delegar la implementación de introducción real de las interfaces introducidas, ocultando el uso de
interceptación
para este propósito. Puede establecer un delegado para cualquier objeto utilizando un argumento de constructor.
El
delegado predeterminado (cuando se utiliza un constructor sin argumentos) es this
. Por lo tanto, en
el
siguiente ejemplo, el delegado es una subclase LockMixin
de la clase DelegatingIntroductionInterceptor
.
Dado un delegado (por defecto, él mismo), una instancia de DelegatingIntroductionInterceptor
busca
todas las interfaces implementadas por el delegado (excepto IntroductionInterceptor
) y admite
introducciones para cualquiera de ellas. Las subclases como LockMixin
pueden llamar al método
suppressInterface(Class
intf)
para suprimir interfaces que no deberían estar abiertas. Sin embargo, no importa cuántas
interfaces
el IntroductionInterceptor
esté dispuesto a admitir, el IntroductionAdvisor
utilizado
controla qué interfaces realmente estarán expuestas. La interfaz expuesta oculta cualquier implementación de la
misma interfaz por parte de un objeto.
Por lo tanto, LockMixin
extiende DelegatingIntroductionInterceptor
e implementa Lockable
. sí mismo. La superclase determina automáticamente que se puede admitir la
inserción de Lockable
, por lo que no es necesario especificarlo. De esta manera podemos introducir
cualquier número de interfaces.
Tenga en cuenta el uso de la variable de instancia locked
. Esto
le permite agregar efectivamente un estado adicional al almacenado en el objeto de destino.
El siguiente
ejemplo muestra un ejemplo de la clase LockMixin
:
public class LockMixin extends DelegatingIntroductionInterceptor implements Lockable {
private boolean locked;
public void lock() {
this.locked = true;
}
public void unlock() {
this.locked = false;
}
public boolean locked() {
return this.locked;
}
public Object invoke(MethodInvocation invocation) throws Throwable {
if (locked() && invocation.getMethod().getName().indexOf("set") == 0) {
throw new LockedException();
}
return super.invoke(invocation);
}
}
class LockMixin : DelegatingIntroductionInterceptor(), Lockable {
private var locked: Boolean = false
fun lock() {
this.locked = true
}
fun unlock() {
this.locked = false
}
fun locked(): Boolean {
return this.locked
}
override fun invoke(invocation: MethodInvocation): Any? {
if (locked() && invocation.method.name.indexOf("set") == 0) {
throw LockedException()
}
return super.invoke(invocation)
}
}
A menudo no es necesario anular el método invoke()
. Una implementación de DelegatingIntroductionInterceptor
(que llama al método delegate
si ese método está presente y, en caso contrario, va al punto de
conexión) suele ser suficiente. En este caso, necesitamos agregar una verificación: no se puede llamar al
configurador si está en modo bloqueado.
La inyección requerida debe contener solo una instancia de LockMixin
e indicar las interfaces
ingresadas (en este caso solo Lockable
). Un ejemplo más complejo podría contener una referencia a
un interceptor de entrada (que se definiría como un prototipo). En este caso, no hay configuración para LockMixin
,
por lo que lo creamos usando new
. El siguiente ejemplo muestra nuestra clase
LockMixinAdvisor
:
public class LockMixinAdvisor extends DefaultIntroductionAdvisor {
public LockMixinAdvisor() {
super(new LockMixin(), Lockable.class);
}
}
clase LockMixinAdvisor: DefaultIntroductionAdvisor(LockMixin(), Lockable::class.java)
Este EA se puede utilizar sin interferencias ya que no requiere ninguna configuración. (Sin embargo, no puede
utilizar IntroductionInterceptor
sin IntroductionAdvisor
). Como suele ser el caso con
las introducciones, primero se debe crear una instancia del EA porque guarda el estado. Requerimos una instancia
separada de LockMixinAdvisor
y, por lo tanto, de LockMixin
, para cada objeto
recomendado. El asesor incluye parte del estado del objeto aconsejado.
Podemos aplicar este asesor programáticamente usando el método Advised.addAdvisor()
o (la forma
recomendada) en la configuración XML. , como cualquier otro asesor. Todas las opciones de creación de proxy que
se describen a continuación, incluidos los "creadores de proxy automático", manejan las inyecciones y los mixins
con estado correctamente.
6.3. API Spring Advisor
En Spring, un asesor es un aspecto que contiene solo un objeto de asesoramiento asociado con una expresión de segmento.
Excepto en el caso especial de introducción, se puede utilizar cualquier asesor. con algún consejo. org.springframework.aop.support.DefaultPointcutAdvisor
es la clase de asesor más utilizada. Se puede utilizar con MethodInterceptor
,
BeforeAdvice
o ThrowsAdvice
.
En Spring, puedes combinar tipos de asesores y sugerencias. en un único proxy AOP. Por ejemplo, puede utilizar la sugerencia "captura", la sugerencia "lanzar excepción" y la sugerencia "antes" en la misma configuración de proxy. Spring crea automáticamente la cadena necesaria de interceptores.
GO TO FULL VERSION