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).

El estilo de configuración <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:

Java

public void monitor(Object service) {
    // ...
}
Kotlin

fun monitor(service: Any) {
    // ...
}

Al combinar un segmento subexpresiones, usar &amp;&amp; en un documento XML es inconveniente, por lo que en lugar de &amp;&amp;, || 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:

Java
public void doAccessCheck(Object retVal) {...
Kotlin
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:

Java
public void doRecoveryActions(DataAccessException dataAccessEx) {...
Kotlin
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:

Java

public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
    // inicia el cronómetro
    Object retVal = pjp.proceed();
    // detener el cronómetro
    return retVal;
}
Kotlin

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:

Java

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

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:

Java

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

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:

Java

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

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:

Java

public void recordUsage(UsageTracked usageTracked) {
    usageTracked.incrementUseCount();
}
Kotlin

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:

Java
UsageTracked useTracked = (UsageTracked) context.getBean("myService"); 
Kotlin
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):

Java

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

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.

Esta clase es idéntico al que se usó en el ejemplo para @AspectJ, pero con las anotaciones eliminadas.

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:

Java

@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {
    // marker annotation
}
Kotlin

@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)"/>