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:aspecto> </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" expresión="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" expresión="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" expresión="execution(*
        com.xyz.myapp.service.*.*(..))"/> ... </aop:aspecto> </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" expresión="execution(* com.xyz.myapp.service.*.*(..)) &amp;&amp; este(servicio)"/> <aop:before pointcut-ref="businessService" método="monitor"/> ... </aop:aspecto> </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
monitor de vacío público (servicio de objetos) { // ... }
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 y, o 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" expresión="execution(* com.xyz.myapp.service.*.*(..)) y este(servicio)"/> <aop:before pointcut-ref="businessService" método="monitor"/> ... </aop:aspecto> </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" método="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.*.*(..))" método="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:después de regresar pointcut-ref="dataAccessOperation" método="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:después de regresar pointcut-ref="dataAccessOperation" return="retVal" método="doAccessCheck"/> ... </aop:aspect>

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:después de lanzar pointcut-ref="dataAccessOperation" método="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:después de lanzar pointcut-ref="dataAccessOperation" throwing="dataAccessEx" método="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:después de pointcut-ref="dataAccessOperation"
            método="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=" un frijol"> <aop:around pointcut-ref="businessService" método="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
Objeto público doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { / / inicia el cronómetro Objeto retVal = pjp.proceed(); // detener el cronómetro return retVal; }
Kotlin
fun doBasicProfiling(pjp: ProceedingJoinPoint): Any { // inicia el cronómetro val retVal = pjp .proceed() // detener el cronómetro 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() y @annotation(auditable)" método="auditoría" 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
paquete x.y.service; interfaz pública PersonService { Persona getPerson(String personName, int age); } public class DefaultPersonService implementa PersonService { public Person getPerson(String name, int age) { return new Person(nombre, edad); } }
Kotlin
paquete x.y.service interfaz PersonService { fun getPerson(personName: String, age: Int) : Persona } clase DefaultPersonService : PersonService { fun getPerson(nombre: String, edad: Int): Persona { return Persona(nombre, edad) } }

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
paquete x.y; importar org.aspectj.lang.ProceedingJoinPoint; importar org.springframework.util.StopWatch; public class SimpleProfiler { perfil de objeto público (llamada ProceedingJoinPoint, nombre de cadena, edad int) throws Throwable { Reloj StopWatch = new StopWatch ("Perfiles para '" + nombre + "' y '" + edad + "'"); prueba { clock.start(call.toShortString()); devolver llamada.proceder(); } finalmente { reloj.parada(); System.out.println(reloj.prettyPrint()); } } }
Kotlin
importar org.aspectj.lang.ProceedingJoinPoint importar org.springframework.util.StopWatch clase SimpleProfiler { perfil divertido (llamada: ProceedingJoinPoint, nombre: Cadena, edad: Int): Cualquiera { val reloj = StopWatch("Creación de perfiles para '$nombre' y '$edad'") intente { reloj.start(call.toShortString( )) return call.proceed() } finalmente { 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"> <!-- este es el objeto que será representado por el marco Spring AOP --> <bean id="personService" class="x.y.service.DefaultPersonService"/> <!-- este es en realidad el consejo en sí --> <bean id="profiler" class="x.y.SimpleProfiler"/> <aop:config> <aop:aspect ref="profiler"> <aop:pointcut id="theExecutionOfSomePersonServiceMethod" expresión="ejecución(* x.y.service.PersonService.getPerson(String,int)) y args(nombre, edad)"/> <aop:around pointcut-ref="theExecutionOfSomePersonServiceMethod" método="perfil"/> </aop:aspecto> </aop:config> </beans>

Considere el siguiente script del controlador:

Java
importar org.springframework.beans.factory.BeanFactory; importar org.springframework.context.support.ClassPathXmlApplicationContext; importar x.y.service.PersonService; arranque de clase final pública { principal vacío estático público (cadena final [] args) lanza excepción { BeanFactory ctx = new ClassPathXmlApplicationContext("x/y/plain.xml"); PersonService persona = (PersonService) ctx.getBean("personService"); persona.getPersona("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 'Perfiles para 'Pengo' y '12': tiempo de ejecución (millis) = 0 ------------------------------- - --------- ms % Nombre de la tarea ------------------------------------ - ---- 00000 ? ejecución(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 alrededor y consejo antes 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-parentstypes-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() y this(usageTracked)" método="recordUsage"/> </aop:aspect>

La clase que sirve como base para el bean UseTracking contendrá el siguiente método:

Java
public void
        recordUsage(UsageTracked useTracked) { useTracked.incrementUseCount(); }
Kotlin
fun recordUsage(usageTracked: UsageTracked) { useTracked.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" expresión="execution(* com.xyz.myapp.service.*.*(..))"/> <aop:advisor pointcut-ref="businessService" asesoramiento-ref="tx-advice"/> </aop:config> <tx:advice id="tx-advice"> <tx:atributos> <tx:nombre del método="*" propagation="REQUIRED"/> </tx:atributos> </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
clase pública ConcurrentOperationExecutor implementa Ordenado {privado static final int DEFAULT_MAX_RETRIES = 2; privado int maxRetries = DEFAULT_MAX_RETRIES; orden int privada = 1; public void setMaxRetries(int maxRetries) { this.maxRetries = maxRetries; } public int getOrder() { return this.order; } public void setOrder(int orden) { this.order = orden; } objeto público doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable { int numAttempts = 0; PessimisticLockingFailureException lockFailureException; hacer { numAttempts++; prueba {return pjp.proceed(); } catch(PessimisticLockingFailureException ex) { lockFailureException = ex; } } while(numAttempts <= this.maxRetries); lanzar lockFailureException; } }
Kotlin
class ConcurrentOperationExecutor: Ordenado { valor privado DEFAULT_MAX_RETRIES = 2 var privado maxRetries = DEFAULT_MAX_RETRIES var privado orden = 1 fun setMaxRetries(maxRetries: Int) { this.maxRetries = maxRetries } anular fun getOrder(): Int { return this.order } fun setOrder(order: Int) { this.order = orden } fun doConcurrentOperation(pjp: ProceedingJoinPoint ): Cualquiera { var numAttempts = 0 var lockFailureException: PessimisticLockingFailureException do { numAttempts++ try { return pjp.proceed() } catch (ej: 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" expresión="execution(* com.xyz.myapp.service.*.*(..))"/> <aop:around pointcut-ref="idempotentOperation" método="doConcurrentOperation"/> </aop:aspecto> </aop:config> <bean id="concurrentOperationExecutor" class="com.xyz.myapp.service.impl.ConcurrentOperationExecutor"> <nombre de propiedad="maxRetries" valor="3"/> <nombre de propiedad="orden" valor="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 { // anotación de marcador }
Kotlin
@Retention(AnnotationRetention.RUNTIME) clase de anotación Idempotente { // anotación de marcador }

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" expresión="ejecución(* com.xyz.myapp.service . *.*(..)) y @annotation(com.xyz.myapp.service.Idempotent)"/>