CodeGym /Cursos Java /Módulo 5. Spring /API de sugerencias en primavera

API de sugerencias en primavera

Módulo 5. Spring
Nivel 5 , Lección 9
Disponible

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:

Java

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;
    }
}
Kotlin

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:

Java
        
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;
    }
}
Kotlin

class CountingBeforeAdvice : MethodBeforeAdvice {
    var count: Int = 0
    override fun before(m: Method, args: Array<Any>, target: Any?) {
        ++count
    }
}
La sugerencia "antes" se puede utilizar con cualquier segmento.

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:

Java

public class RemoteThrowsAdvice implements ThrowsAdvice {
    public void afterThrowing(RemoteException ex) throws Throwable {
        // Do something with the remote exception
    }
}
Kotlin

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:

Java

public class ServletThrowsAdviceWithArguments implements ThrowsAdvice {
    public void afterThrowing(Method m, Object[] args , Object target, ServletException ex) {
        // Do something with all the arguments
    }
}
Kotlin

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:

Java

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
    }
}
Kotlin

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
    }
}
If el método tip "lanzar excepción" lanza una excepción en sí misma, anula la excepción original (es decir, modifica la excepción lanzada para el usuario). Una excepción de anulación suele ser una RuntimeException, que es compatible con cualquier firma de método. Sin embargo, si el método tip "lanzar excepción" genera una excepción marcada, debe coincidir con las excepciones declaradas del método de destino y, por lo tanto, estar vinculado hasta cierto punto por las firmas específicas del método de destino. ¡No lance una excepción marcada no declarada que sea incompatible con la firma del método de destino!
La opción "lanzar una excepción" tip se puede usar con cualquier segmento.

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:

Java

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;
    }
}
Kotlin

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.

El consejo "después del retorno" se puede usar con cualquier rebanada.

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:

Java

public interface Lockable {
    void lock();
    void unlock();
    boolean locked();
}
Kotlin

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:

Java

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);
    }
}
Kotlin

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:

Java

public class LockMixinAdvisor extends DefaultIntroductionAdvisor {
    public LockMixinAdvisor() {
        super(new LockMixin(), Lockable.class);
    }
}
Kotlin
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.

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION