Primavera AOP utiliza servidores proxy dinámicos JDK o CGLIB para crear un proxy para un objetivo determinado. Los proxies dinámicos JDK están integrados en el JDK, mientras que CGLIB es una biblioteca de definición de clases de código abierto normal (reempaquetada en spring-core).

Si el destino proxy implementa aunque sea una interfaz, Se utiliza un proxy JDK dinámico. Todas las interfaces implementadas por el tipo de destino son proxy. Si el objeto de destino no implementa ninguna interfaz, se crea un proxy CGLIB.

Si desea forzar el proxy CGLIB (por ejemplo, para representar todos los métodos definidos en el objeto de destino, no solo los implementados por su interfaces), esto también es factible. Sin embargo, se deben considerar las siguientes cuestiones:

  • En el caso de CGLIB, los métodos final no pueden recibir sugerencias porque no pueden anularse en las subclases creadas. en tiempo de ejecución.

  • A partir de Spring 4.0, el constructor del objeto proxy NO se llama dos veces ya que el proxy CGLIB se crea una instancia a través de Objenesis. Si su JVM no permite omitir el constructor, es posible que observe llamadas dobles y las correspondientes entradas de registro de depuración del soporte AOP de Spring.

Para forzar el uso del proxy CGLIB, configure el valore el atributo proxy-target-class del elemento <aop:config> en verdadero, como se muestra a continuación:

<aop:config proxy-target-class="true">
    <!-- other beans defined here... -->
</aop:config>

Para forzar el proxy CGLIB cuando se utiliza soporte de proxy automático en @AspectJ, establezca el atributo de proxy-target-class del elemento <aop:aspectj-autoproxy> en true, como se muestra a continuación:

<aop:aspectj-autoproxy proxy-target-class="true"/> ;

Varias secciones <aop:config/> se contraen en un único creador de proxy automático en tiempo de ejecución, que aplica la configuración de proxy más estricta especificada en cualquiera de las secciones <aop:config/>. (normalmente de diferentes archivos de definición de beans XML). Esto también se aplica a los elementos <tx:annotation-driven/> y <aop:aspectj-autoproxy/>.

Para más comprensión, uso de proxy-target-class="true" en <tx:annotation-driven/>, <aop:aspectj-autoproxy/> elements o <aop:config/> fuerza el proxy CGLIB para los tres elementos.

Comprender el proxy AOP

Spring AOP se basa en un proxy. Es fundamental que comprenda la semántica de lo que significa la última declaración antes de escribir sus propios aspectos o utilizar cualquiera de los aspectos basados en Spring AOP que vienen con Spring Framework.

Primero consideremos un escenario en que tiene una referencia directa ordinaria, típica, no proxy, no especial, a un objeto, como se muestra en el siguiente fragmento de código:

Java
public class SimplePojo implements Pojo {
    public void foo() {
        // la siguiente llamada al método es una llamada directa a la
        referencia "this"  this.bar();
    } public void bar() {
        // un poco de lógica...
    }
}
Kotlin
class SimplePojo : Pojo {
    fun foo() {
        // la siguiente llamada al método es una llamada directa a la referencia "this"
        this.bar()
    }
    fun bar() {
        // un poco de lógica...
    }
}

Si llama a un método en una referencia de objeto, entonces el método se llama directamente en esa referencia de objeto, como se muestra en la siguiente imagen y listado:

Java
public class Main {
    public static void main(String[] args) {
        Pojo pojo = new SimplePojo();
        // esta es una llamada directa al método por referencia "pojo
        pojo.foo();
    }
}}
Kotlin
fun main() {
    val pojo = SimplePojo()
    // este es un directo llamada al método por referencia "pojo"
    pojo.foo()
}

La situación cambia un poco si la referencia contenida en el código del cliente es un proxy. Considere el siguiente diagrama y fragmento de código:

Java
public class Main {
    public static void main(String[] args) {
        ProxyFactory factory = new ProxyFactory(new SimplePojo());
        factory.addInterface(Pojo.class);
        factory.addAdvice(new RetryAdvice());
        Pojo pojo = (Pojo) factory.getProxy();
        // ¡Esta es una llamada al método para el proxy!
        pojo.foo();
    }
}
Kotlin
fun main() {
    val factory = ProxyFactory(SimplePojo())
    factory.addInterface (Pojo::class.java)
    factory.addAdvice(RetryAdvice())
    val pojo = factory.proxy as Pojo
    // ¡esta es una llamada al método para el proxy!
    pojo.foo()
}

Lo más importante que hay que entender aquí es que el código del cliente está dentro de main(..) El método de la clase Main contiene un enlace al proxy. Esto significa que las llamadas a métodos en esta referencia de objeto son llamadas de proxy. Como resultado, el proxy puede delegar todos los interceptores (consejos) que sean relevantes para esa llamada a método en particular. Sin embargo, una vez que la llamada llega al objeto de destino (en este caso el enlace SimplePojo), todas las llamadas a métodos que puede realizar sobre sí mismo, como this.bar() o this.foo() se llamará en la referencia this, no en el proxy. Esto tiene consecuencias importantes. Esto significa que la llamada en sí no provocará que se ejecute el consejo asociado con la llamada al método.

Está bien, pero ¿qué puedes hacer al respecto? El mejor enfoque (el término "mejor" se usa libremente aquí) es refactorizar su código para que no se produzca la llamada en sí. Esto requiere algo de esfuerzo de su parte, pero es el mejor método y el menos invasivo. El siguiente enfoque es absolutamente terrible y no estamos seguros de que valga la pena centrarse en él precisamente porque es terrible. Puedes (por más doloroso que nos parezca) vincular completamente la lógica dentro de tu clase a Spring AOP, como se muestra en el siguiente ejemplo:

Java
public class SimplePojo implements Pojo {
    public void foo() {
        // funciona, pero... ¡uf!
        ((Pojo) AopContext.currentProxy()).bar();
    } public void bar() {
        // un poco de lógica...
    }
}
Kotlin
class SimplePojo : Pojo {
    fun foo() {
         // funciona, pero... ¡uf!
        (AopContext.currentProxy() as Pojo).bar()
    } fun bar() {
            // un poco de lógica...
    }
}

Este enfoque Esto vinculará completamente su código a Spring AOP, y la clase misma sabrá que se está utilizando en un contexto AOP, lo que va en contra de AOP. También requerirá alguna configuración adicional al crear el proxy, como se muestra en el siguiente ejemplo:

Java
public class Main {
    public static void main(String[] args) {
        ProxyFactory factory = new ProxyFactory(new SimplePojo());
        factory.addInterface(Pojo.class);
        factory.addAdvice(new RetryAdvice());
        factory.setExposeProxy(true);
        Pojo pojo = (Pojo) factory.getProxy();
        // ¡Esta es una llamada al método para el proxy!
        pojo.foo();
    }
}
Kotlin
fun main() {
    val factory = ProxyFactory(SimplePojo())
    factory.addInterface (Pojo::class.java)
    factory.addAdvice(RetryAdvice())
    factory.isExposeProxy = true
    val pojo = factory.proxy as Pojo
    // ¡esta es una llamada al método para el proxy!
    pojo.foo()
}

Finalmente, cabe señalar que AspectJ no tiene este problema de autollamada porque no es un AOP basado en proxy. framework.

Creación programática de proxies @AspectJ

Además de declarar aspectos en la configuración usando <aop:config> o <aop:aspectj-autoproxy>, también puede crear proxies mediante programación que proporcionen consejos a los objetivos. En esta parte, queremos centrarnos en la capacidad de crear servidores proxy automáticamente usando aspectos @AspectJ.

Puedes usar la clase org.springframework.aop.aspectj.annotation.AspectJProxyFactory para crear servidores proxy para el objeto de destino que ha sido recomendado por uno o más @AspectJ. El uso básico de esta clase es extremadamente simple, como se muestra en el siguiente ejemplo:

Java
// crea una fábrica que puede generar un proxy para un objetivo determinado Fábrica de
AspectJProxyFactory factory = new AspectJProxyFactory(targetObject);
// agrega un aspecto, la clase debe ser un aspecto @AspectJ
// puedes llamarlo tantas veces como sea necesario con diferentes aspectos
factory.addAspect(SecurityManager.class);
// también se pueden agregar instancias de aspectos existentes, el tipo de objeto proporcionado debe ser un aspecto @AspectJ
factory.addAspect(usageTracker);
// ahora obtenemos el objeto proxy...
MyInterfaceType proxy = factory.getProxy();
Kotlin
// crear una fábrica que pueda generar proxies para dado el objeto de destino
val factory = AspectJProxyFactory(targetObject)
// agrega un aspecto, la clase debe ser un aspecto @AspectJ
// puedes llamarlo tantas veces como quieras con diferentes aspectos
factory.addAspect(SecurityManager::class.java)
// también puedes agregar instancias de aspecto existentes, el tipo de objeto proporcionado debe ser un aspecto @AspectJ
factory.addAspect(usageTracker)
// ahora obtén el objeto proxy...
val proxy = factory.getProxy<Any>()