Si prefiere un formato basado en XML, Spring también proporciona soporte para definir aspectos usando aop
etiquetas de espacio de nombres. Se admiten exactamente las mismas expresiones de corte y tipos de sugerencias que cuando se utiliza el estilo @AspectJ. Por lo tanto, en esta sección nos centraremos en esta sintaxis y referiremos al lector a la descripción de la sección anterior para conocer los detalles específicos de la escritura de expresiones de segmento y la vinculación de parámetros de asesoramiento.
Para usar las etiquetas de espacio de nombres AOP descritas en este sección, debe importar el esquema spring-aop
.
En las configuraciones de Spring, todos los aspectos y elementos asesores deben colocarse en el <aop:config>
elemento (puede tener más de un elemento <aop:config>
en la configuración del contexto de su aplicación). El elemento <aop:config>
puede contener elementos de segmentación, asesor y aspecto (tenga en cuenta que deben declararse en ese orden).
<aop:config>
utiliza activamente el mecanismo de proxy automático de Spring. Esto puede generar problemas (por ejemplo, la sugerencia no estará vinculada) si ya está utilizando un proxy automático explícito usando
BeanNameAutoProxyCreator
o algo similar. Se recomienda utilizar solo el estilo
<aop:config>
o solo el estilo
AutoProxyCreator
y nunca mezclarlos.
Declaración de aspecto
Si está utilizando soporte de esquema, un aspecto es un objeto Java normal definido como un bean en el contexto de su aplicación Spring. La lógica de estado y operación se captura en los campos y métodos del objeto, y la información sobre sectores y sugerencias se captura en XML.
Puedes declarar un aspecto usando el <aop:aspect>
elemento y haga referencia al bean base utilizando el atributo ref
, como se muestra en el siguiente ejemplo:
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
...
</aop:aspect>
</aop:config>
<bean id="aBean" class="...">
...
</bean>
El bean que soporta el aspecto (en este caso aBean
) puede, por supuesto, ser configure e inyecte dependencias como cualquier otro Spring Bean.
Declaración de un segmento
Puede declarar un segmento con nombre dentro de un elemento <aop:config>
, permitiéndole compartir el uso de la definición de segmento en múltiples aspectos y asesores.
Un segmento que representa la ejecución de cualquier servicio empresarial en el nivel de servicio se puede definir de la siguiente manera:
<aop:config>
<aop:pointcut id="businessService"
expression="execution(* com.xyz.myapp.service.*.*(..))"/>
</aop:config>
Tenga en cuenta que la expresión de sector en sí utiliza el mismo lenguaje de expresión de sector de AspectJ. Si utiliza un estilo de declaración basado en esquema, puede hacer referencia a sectores con nombre definidos en tipos (@Aspects) en la expresión del sector. Otra forma de definir el segmento anterior podría verse así:
<aop:config>
<aop:pointcut id="businessService"
expression="com.xyz.myapp.CommonPointcuts.businessService()"/>
</aop:config>
Supongamos que tiene un aspecto CommonPointcuts
.
Declaración de segmentación de datos dentro de un aspecto es muy similar a una declaración de segmentación de datos de nivel superior, como se muestra en el siguiente ejemplo:
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
<aop:pointcut id="businessService"
expression="execution(* com.xyz.myapp.service.*.*(..))"/>
...
</aop:aspect>
</aop:config>
Al igual que @AspectJ, los sectores declarados utilizando el estilo de definición basado en esquema pueden recopilar el contexto del punto de conexión. Por ejemplo, el siguiente segmento recopila el objeto this
como contexto de punto de unión y lo pasa a la punta:
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
<aop:pointcut id="businessService"
expression="execution(* com.xyz.myapp.service.*.*(..)) && this(service)"/>
<aop:before pointcut-ref="businessService" method="monitor"/>
...
</aop:aspect>
</aop:config>
Se debe declarar el consejo para obtener el contexto del punto de conexión recopilado incluyendo parámetros con los nombres apropiados, como se muestra a continuación:
public void monitor(Object service) {
// ...
}
fun monitor(service: Any) {
// ...
}
Al combinar un segmento subexpresiones, usar &&
en un documento XML es inconveniente, por lo que en lugar de &&
, ||
y !
puedes usar las palabras clave and
, or
y not
respectivamente. Por ejemplo, el segmento anterior se puede escribir de la siguiente manera:
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
<aop:pointcut id="businessService"
expression="execution(* com.xyz.myapp.service.*.*(..)) and this(service)"/>
<aop:before pointcut-ref="businessService" method="monitor"/>
...
</aop:aspect>
</aop:config>>
Tenga en cuenta que las segmentaciones de datos definidas de esta manera se refieren a su id
XML y no se pueden utilizar como sectores con nombre para componer sectores compuestos. Por lo tanto, el soporte para sectores con nombre en el estilo de definición basado en esquema es más limitado que en el estilo @AspectJ.
Consejos de declaración
Se utilizan los mismos cinco tipos de consejos para admitir el esquema. -based AOP. como para el estilo @AspectJ, y tienen exactamente la misma semántica.
Aviso previo
El consejo "antes" se ejecuta antes de que se ejecute el método correspondiente. Se declara dentro de <aop:aspect>
usando el elemento <aop:before>
, como se muestra en el siguiente ejemplo:
<aop:aspect id="beforeExample" ref="aBean">
<aop:before
pointcut-ref="dataAccessOperation"
method="doAccessCheck"/>
...
</aop:aspect>
Aquí dataAccessOperation
es el id
del segmento, definido en el nivel superior (<aop:config>
). Para definir una incrustación de sectores, reemplace el atributo pointcut-ref
con el atributo pointcut
, como se muestra a continuación:
<aop:aspect id="beforeExample" ref="aBean">
<aop:before
pointcut="execution(* com.xyz.myapp.dao.*.*(..))"
method="doAccessCheck"/>
...
</aop:aspect>
Como se observó al observar el estilo @AspectJ, el uso de segmentaciones con nombre puede mejorar en gran medida la legibilidad de su código.
El atributo method
identifica el método (doAccessCheck
) que proporciona el cuerpo del consejo. Este método debe definirse para el bean al que hace referencia el elemento de aspecto que contiene la punta. Antes de realizar una operación de acceso a datos (el punto de unión de ejecución del método correspondiente a la expresión de segmento), se llama al método doAccessCheck
en el bean aspecto.
Consejo después de regresar
El consejo "después de regresar" se ejecuta cuando el método coincidente se completa en el orden normal. Se declara dentro de <aop:aspect>
de la misma manera que el consejo anterior. El siguiente ejemplo muestra cómo declararlo:
<aop:aspect id="afterReturningExample" ref="aBean">
<aop:after-returning
pointcut-ref="dataAccessOperation"
method="doAccessCheck"/>
...
</aop:aspect>
Como en el estilo @AspectJ, puede obtener el valor de retorno en el cuerpo del consejo. Para hacer esto, use el atributo returning
para especificar el nombre del parámetro al que se debe pasar el valor de retorno, como se muestra en el siguiente ejemplo:
<aop:aspect id="afterReturningExample" ref="aBean">
<aop:after-returning
pointcut-ref="dataAccessOperation"
returning="retVal"
method="doAccessCheck"/>
...
</aop:aspect>code>
El método doAccessCheck
debe declarar un parámetro llamado retVal
. El tipo de este parámetro limita la búsqueda de coincidencias de la misma manera que se describe para @AfterReturning
. Por ejemplo, no puede declarar la firma de un método de la siguiente manera:
public void doAccessCheck(Object retVal) {...
fun doAccessCheck(retVal: Any) {...
Consejo después del lanzamiento
El consejo "después del lanzamiento" se ejecuta cuando la ejecución del método correspondiente finaliza con el lanzamiento de una excepción. Se declara dentro de <aop:aspect>
usando el elemento after-throwing
, como se muestra en el siguiente ejemplo:
<aop:aspect id="afterThrowingExample" ref="aBean">
<aop:after-throwing
pointcut-ref="dataAccessOperation"
method="doRecoveryActions"/>
...
</aop:aspect>
Como en el estilo @AspectJ, es posible obtener la excepción lanzada en el cuerpo del consejo. Para hacer esto, use el atributo throwing
para especificar el nombre del parámetro al que se debe pasar la excepción, como se muestra en el siguiente ejemplo:
<aop:aspect id="afterThrowingExample" ref="aBean">
<aop:after-throwing
pointcut-ref="dataAccessOperation"
throwing="dataAccessEx"
method="doRecoveryActions"/>
...
</aop:aspect>
El método doRecoveryActions
debe declarar un parámetro llamado dataAccessEx
. El tipo de este parámetro limita la búsqueda de coincidencias de la misma manera que se describe para @AfterThrowing
. Por ejemplo, la firma de un método se puede declarar de la siguiente manera:
public void doRecoveryActions(DataAccessException dataAccessEx) {...
fun doRecoveryActions(dataAccessEx: DataAccessException) {...
Consejo Después (Finalmente)
El consejo Después (Finalmente) se ejecuta independientemente de cómo el método asociado complete la ejecución. Puedes declararlo usando el elemento after
, como se muestra en el siguiente ejemplo:
<aop:aspect id="afterFinallyExample" ref="aBean">
<aop:after
pointcut-ref="dataAccessOperation"
method="doReleaseLock"/>
...
</aop:aspect>
Consejos aproximados
El último tipo de consejo es el "en lugar" consejo El consejo "en su lugar" reemplaza la ejecución del método asociado. Puede ejecutarse antes o después de que se ejecute un método y determinar cuándo, cómo e incluso si el método se ejecuta. El consejo "en lugar" se utiliza a menudo cuando desea separar el estado "antes" y "después" de la ejecución de un método de forma segura para subprocesos, por ejemplo, para iniciar y detener un temporizador.
Utilice siempre la forma de consejo menos influyente que satisfaga sus necesidades.
Utilice siempre la forma de consejo menos influyente que satisfaga sus necesidades requisitos. Por ejemplo, no utilice el consejo "en lugar de" si el consejo "antes" es suficiente para sus necesidades.
Puede declarar un consejo "en lugar" utilizando el elemento aop:around
. El método de asesoramiento debe declarar Object
como su tipo de retorno y el primer parámetro del método debe ser del tipo ProceedingJoinPoint
. En el cuerpo del método del consejo, debe llamar a proceed()
en ProceedingJoinPoint
para ejecutar el método principal. Llamar a proceed()
sin argumentos hará que los argumentos originales de la persona que llama se pasen al método base cuando se llama. Para casos de uso más avanzados, existe una sobrecarga del método proceed()
que toma una matriz de argumentos (Object[]
). Los valores de la matriz se utilizarán como argumentos para el método base cuando se llame.
El siguiente ejemplo muestra cómo declarar una sugerencia "en lugar" en XML:
<aop:aspect id="aroundExample" ref="aBean">
<aop:around
pointcut-ref="businessService"
method="doBasicProfiling"/>
...
</aop:aspect>
La implementación del consejo doBasicProfiling
puede ser exactamente la misma que en el ejemplo para @ AspectJ (menos la anotación, por supuesto), como se muestra en el siguiente ejemplo:
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
// inicia el cronómetro
Object retVal = pjp.proceed();
// detener el cronómetro
return retVal;
}
fun doBasicProfiling(pjp: ProceedingJoinPoint): Any {
// start the stopwatch
val retVal = pjp .proceed()
// stop the stopwatch
return pjp.proceed()
}
Opciones de consejos
Estilo de declaración basado en esquemas es totalmente compatible con los consejos escritos de la misma manera que se describe para el soporte en el caso de @AspectJ: asignando parámetros de segmento por nombre a los parámetros del método de consejo. Si necesita especificar explícitamente nombres de argumentos para los métodos de asesoramiento (sin utilizar las estrategias de detección descritas anteriormente), puede hacerlo utilizando el atributo arg-names
del elemento de asesoramiento, que se trata de la misma manera que el atributo argNames
en la anotación del consejo. El siguiente ejemplo muestra cómo especificar el nombre del argumento en XML:
<aop:before
pointcut="com.xyz.lib.Pointcuts.anyPublicMethod() and @annotation(auditable)"
method="audit"
arg-names="auditable"/>
El atributo arg-names
acepta una lista de nombres de parámetros separados por comas.
El siguiente, ligeramente Un ejemplo más complejo del enfoque, basado en XSD, demuestra algunos consejos utilizados en combinación con una serie de parámetros fuertemente tipados:
package x.y.service;
public interface PersonService {
Person getPerson(String personName, int age);
}
public class DefaultPersonService implements PersonService {
public Person getPerson(String name, int age) {
return new Person(name, age);
}
}
package x.y.service
interface PersonService {
fun getPerson(personName: String, age: Int) : Person
}
class DefaultPersonService : PersonService {
fun getPerson(name: String, age: Int): Person {
return Person(name, age)
}
}
Luego viene el aspecto. Tenga en cuenta que el método profile(..)
toma varios parámetros fuertemente tipados, el primero de los cuales es el punto de conexión utilizado para continuar llamando al método. La presencia de este parámetro indica que profile(..)
debe usarse como una sugerencia around
, como se muestra en el siguiente ejemplo:
package x.y;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.util.StopWatch;
public class SimpleProfiler {
public Object profile(ProceedingJoinPoint call, String name, int age) throws Throwable {
StopWatch clock = new StopWatch("Profiling for '" + name + "' and '" + age + "'");
try {
clock.start(call.toShortString());
return call.proceed();
} finally {
clock.stop();
System.out.println(clock.prettyPrint());
}
}
}
import org.aspectj.lang.ProceedingJoinPoint
import org.springframework.util.StopWatch
class SimpleProfiler {
fun profile(call: ProceedingJoinPoint, name: String, age: Int): Any {
val clock = StopWatch("Profiling for '$name' and '$age'")
try {
clock.start(call.toShortString())
return call.proceed()
} finally {
clock.stop()
println(clock.prettyPrint())
}
}
}
Finalmente, lo siguiente La configuración XML de ejemplo afecta la ejecución del consejo anterior para un punto de conexión específico:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop ="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- this is the object that will be proxied by the Spring AOP framework -->
<bean id="personService" class="x.y.service.DefaultPersonService"/>
<!-- this is actually the advice itself -->
<bean id="profiler" class="x.y.SimpleProfiler"/>
<aop:config>
<aop:aspect ref="profiler">
<aop:pointcut id="theExecutionOfSomePersonServiceMethod"
expression="execution(* x.y.service.PersonService.getPerson(String,int))
and args(name, age)"/>
<aop:around pointcut-ref="theExecutionOfSomePersonServiceMethod"
method="profile"/>
</aop:aspect>
</aop:config>
</beans>
Considere el siguiente script del controlador:
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import x.y.service.PersonService;
public final class Boot {
public static void main(final String[] args) throws Exception {
BeanFactory ctx = new ClassPathXmlApplicationContext("x/y/plain.xml");
PersonService person = (PersonService) ctx.getBean("personService");
person.getPerson("Pengo", 12);
}
}
fun main() {
val ctx = ClassPathXmlApplicationContext("x/y/plain.xml")
val person = ctx.getBean("personService") as PersonService
person.getPerson("Pengo", 12)
}}
Con esta clase de arranque obtendríamos un resultado de ejecución similar al siguiente con salida estándar:
StopWatch 'Profiling for 'Pengo' and '12': running time (millis) = 0 ----------------------------------------- ms % Task name ----------------------------------------- 00000 ? execution(getFoo)
Sugerencias de orden
Si es necesario ejecutar varias sugerencias en el mismo punto de conexión (método de ejecución), se describen las reglas de ordenación en la sección "Consejos para realizar pedidos". La precedencia entre aspectos se determina mediante el atributo order
en el elemento <aop:aspect>
, o agregando la anotación @Order
al bean. apoyando el aspecto, o implementando un binomio de la interfaz Ordered
.
A diferencia de las reglas de precedencia para consejos métodos definidos en la misma clase, anotados con @Aspect
, si dos consejos definidos en el mismo elemento <aop:aspect>
se van a ejecutar en el mismo punto de unión, la precedencia está determinada por el orden en el que se declaran los elementos de aviso en el elemento adjunto <aop:aspect>
desde el nivel de precedencia más alto al más bajo.
Por ejemplo, si hay son elementos de consejo around
y consejo before
definidos en el mismo elemento <aop:aspect>
que se aplican al mismo punto de unión si lo desea. Para garantizar que el consejo around
tenga un nivel de precedencia más alto que el consejo before
, se debe declarar el elemento <aop:around>
. antes del elemento <aop:before>
.
Como regla general, si encuentra que hay varios consejos definidos en un solo <aop :aspect>
elemento que se aplica al mismo punto de unión, considere combinar dichos métodos de asesoramiento en un método de asesoramiento para cada punto de unión en cada elemento <aop:aspect>
, o refactorizar los consejos en elementos <aop:aspect>
separados, que se pueden ordenar a nivel de aspecto.
Introducciones
Introducciones (conocidas en AspectJ como declaraciones entre tipos) permiten que un aspecto declare que los objetos proporcionados con el consejo implementan una interfaz determinada y proporcionan una implementación de esa interfaz en nombre de esos objetos.
Puedes implementar la introducción usando el Elemento aop:declare-parents
dentro del elemento aop:aspect
. Puede utilizar el elemento aop:declare-parents
para declarar que los tipos coincidentes tienen un nuevo tipo principal (de ahí el nombre). Por ejemplo, dada la interfaz UsageTracked
y la implementación DefaultUsageTracked
de esa interfaz, el siguiente aspecto declara que todos los implementadores de la interfaz de servicio también implementan la interfaz UsageTracked
. (Por ejemplo, para abrir estadísticas a través de JMX).
<aop:aspect id="usageTrackerAspect" ref="usageTracking">
<aop:declare-parents
types-matching="com.xzy.myapp.service.*+"
implement-interface="com.xyz.myapp.service.tracking.UsageTracked"
default-impl="com.xyz.myapp.service.tracking.DefaultUsageTracked"/>
<aop:before
pointcut="com.xyz.myapp.CommonPointcuts.businessService()
and this(usageTracked)"
method="recordUsage"/>
</aop:aspect>
La clase que sirve como base para el bean UseTracking
contendrá el siguiente método:
public void recordUsage(UsageTracked usageTracked) {
usageTracked.incrementUseCount();
}
fun recordUsage(usageTracked: UsageTracked) {
usageTracked.incrementUseCount()
}
La interfaz que se debe implementar está determinada por el atributo implement-interface
. El valor del atributo types-matching
es la plantilla de tipo AspectJ. Cualquier bean del tipo apropiado implementa la interfaz UsageTracked
. Tenga en cuenta que en el consejo "antes" del ejemplo anterior, los service beans se pueden utilizar directamente como implementaciones de la interfaz UsageTracked
. Para acceder al bean mediante programación, puede escribir lo siguiente:
UsageTracked useTracked = (UsageTracked) context.getBean("myService");
val useTracked = context.getBean("myService") as UsageTracked
Modelos de instanciación de aspectos
El único modelo de instanciación admitido para aspectos definidos por esquema es el modelo de instanciación singleton. Es posible que en futuras versiones se agregue soporte para otros modelos de creación de instancias.
Asesores
El concepto de "asesores" proviene de las funciones de soporte de AOP definidas en Spring y no tiene un equivalente directo en AspectJ. Un asesor es como un pequeño aspecto autónomo al que se le proporciona un único consejo. El propio consejo está representado por un bean y debe implementar una de las interfaces del consejo. Los asesores pueden aprovechar las expresiones de segmento de AspectJ.
Spring admite el concepto de asesor utilizando el elemento <aop:advisor>
. Se utiliza con mayor frecuencia junto con el asesoramiento transaccional, que también tiene su propio espacio de nombres en Spring. El siguiente ejemplo muestra el asesor:
<aop:config>
<aop:pointcut id="businessService"
expression="execution(* com.xyz.myapp.service.*.*(..))"/>
<aop:advisor
pointcut-ref="businessService"
advice-ref="tx-advice"/>
</aop:config>
<tx:advice id="tx-advice">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
Además del atributo pointcut-ref
utilizado en el ejemplo anterior, también puede use pointcut
para definir la incrustación de la expresión de corte.
Para definir la antigüedad del asesor para que el asesor pueda participar en el pedido, use order
atributo para definir el valor del asesor Ordered
.
Ejemplo de esquema AOP
Esta sección muestra cómo se vería el ejemplo de reintento de falla de bloqueo concurrente de Un ejemplo de AOP si se reescribió utilizando soporte de esquema.
A veces, los servicios empresariales pueden fallar debido a problemas de concurrencia (como un punto muerto). Si se repite la operación, lo más probable es que tenga éxito la próxima vez que lo intente. Para los servicios empresariales donde es apropiado reintentar una operación bajo estas condiciones (operaciones idempotentes que no necesitan volver al usuario para resolver el conflicto), queremos reintentar la operación de una manera clara sin que el cliente vea un PessimisticLockingFailureException
. Este es un requisito que obviamente abarca múltiples servicios a nivel de servicio y, por lo tanto, es ideal para implementarlo a través de un aspecto.
Dado que necesitamos repetir la operación, debemos usar el consejo "en lugar" para que que podemos llamar al método proceder
varias veces. La siguiente lista muestra una implementación de aspecto básico (que es una clase Java normal que utiliza soporte de esquema):
public class ConcurrentOperationExecutor implements Ordered {
private static final int DEFAULT_MAX_RETRIES = 2;
private int maxRetries = DEFAULT_MAX_RETRIES;
private int order = 1;
public void setMaxRetries(int maxRetries) {
this.maxRetries = maxRetries;
}
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
int numAttempts = 0;
PessimisticLockingFailureException lockFailureException;
do {
numAttempts++;
try {
return pjp.proceed();
}
catch(PessimisticLockingFailureException ex) {
lockFailureException = ex;
}
} while(numAttempts <= this.maxRetries);
throw lockFailureException;
}
}
class ConcurrentOperationExecutor : Ordered {
private val DEFAULT_MAX_RETRIES = 2
private var maxRetries = DEFAULT_MAX_RETRIES
private var order = 1
fun setMaxRetries(maxRetries: Int) {
this.maxRetries = maxRetries
}
override fun getOrder(): Int {
return this.order
}
fun setOrder(order: Int) {
this.order = order
}
fun doConcurrentOperation(pjp: ProceedingJoinPoint): Any {
var numAttempts = 0
var lockFailureException: PessimisticLockingFailureException
do {
numAttempts++
try {
return pjp.proceed()
} catch (ex: PessimisticLockingFailureException) {
lockFailureException = ex
}
} while (numAttempts <= this.maxRetries)
throw lockFailureException
}
}
Tenga en cuenta que el aspecto implementa la interfaz Ordered
para que podamos establecer el nivel de prioridad del aspecto más alto que el consejo de transacciones. (Necesitamos , para que cada vez que vuelvas a intentar la transacción sea una nueva). Las propiedades maxRetries
y order
están configuradas por Spring. La acción principal ocurre en el método doConcurrentOperation
del consejo "en lugar". Intentemos continuar la ejecución. En caso de un error que genera una excepción PessimisticLockingFailureException
, lo reintentamos si no se han agotado todos los intentos.
La configuración de Spring correspondiente se ve así:
<aop:config>
<aop:aspect id="concurrentOperationRetry" ref="concurrentOperationExecutor">
<aop:pointcut id="idempotentOperation"
expression="execution(* com.xyz.myapp.service.*.*(..))"/>
<aop:around
pointcut-ref="idempotentOperation"
method="doConcurrentOperation"/>
</aop:aspect>
</aop:config>
<bean id="concurrentOperationExecutor"
class="com.xyz.myapp.service.impl.ConcurrentOperationExecutor">
<property name="maxRetries" value="3"/>
<property name="order" value="100"/>
</bean>
Tenga en cuenta que en este punto asumimos que todos los servicios empresariales son idempotentes. Si este no es el caso, puede modificar el aspecto para que solo repita operaciones verdaderamente idempotentes introduciendo la anotación Idempotent
y usándola para anotar la implementación de operaciones de utilidad, como se muestra en el siguiente ejemplo:
@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {
// marker annotation
}
@Retention(AnnotationRetention.RUNTIME)
annotation class Idempotent {
// marker annotation
}
Cambiar el aspecto para repetir solo operaciones idempotentes implica refinar la expresión de segmento para que solo coincidan las operaciones @Idempotent
, como se muestra a continuación:
<aop:pointcut id="idempotentOperation"
expression="execution(* com.xyz.myapp.service. *.*(..)) and
@annotation(com.xyz.myapp.service.Idempotent)"/>
GO TO FULL VERSION