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:
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...
}
}
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:
![](https://cdn.codegym.cc/images/article/766255c1-68e6-48a5-8c32-2eff24488e98/1024.jpeg)
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();
}
}}
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:
![](https://cdn.codegym.cc/images/article/bf53f886-eff2-400f-a965-821805e83846/800.jpeg)
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();
}
}
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:
public class SimplePojo implements Pojo {
public void foo() {
// funciona, pero... ¡uf!
((Pojo) AopContext.currentProxy()).bar();
} public void bar() {
// un poco de lógica...
}
}
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:
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();
}
}
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:
// 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();
// 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>()