If you prefer an XML-based format, Spring also provides support for defining aspects using aop namespace tags. The exact same slice expressions and tip types are supported as when using the @AspectJ style. Therefore, in this section we will focus on this syntax and refer the reader to the description in the previous section for the specifics of writing slice expressions and advice parameter binding.

To use the AOP namespace tags described in this section, you must import the spring-aop schema.

In Spring configurations, all aspects and advisors elements must be placed in the <aop:config> element ( You can have more than one <aop:config> element in your application context configuration). The <aop:config> element can contain slicer, advisor, and aspect elements (note that they must be declared in that order).

The configuration style <aop:config> actively uses the Spring auto-proxy mechanism. This can lead to problems (for example, the tip will not be linked) if you are already using explicit autoproxy using BeanNameAutoProxyCreator or something similar. It is recommended to use only the <aop:config> style or only the AutoProxyCreator style and never mix them.

Aspect Declaration

If you are using schema support, an aspect is a regular Java object defined as a bean in the context of your Spring application. State and operation logic are captured in the object's fields and methods, and information about slices and tips is captured in XML.

You can declare an aspect using the <aop:aspect> element, and reference the base bean using the ref attribute, as shown in the following example:


<aop:config>
    <aop:aspect id="myAspect" ref="aBean">
        ...
    </aop:aspect>
</aop:config>
<bean id="aBean" class="...">
    ...
</bean>

The bean that supports the aspect (in this case aBean) can of course be configured and inject dependencies like any other Spring bean.

Declaring a Slice

You can declare a named slice inside a <aop:config> element, allowing you to share use the slice definition in multiple aspects and advisors.

A slice representing the execution of any business service at the service level can be defined as follows:


<aop:config>
    <aop:pointcut id="businessService"
        expression="execution(* com.xyz.myapp.service.*.*(..))"/>
</aop:config>

Note that the slice expression itself uses the same AspectJ slice expression language. If you use a schema-based declaration style, you can reference named slices defined in types (@Aspects) in the slice expression. Another way to define the above slice could look like this:


<aop:config>
    <aop:pointcut id="businessService"
        expression="com.xyz.myapp.CommonPointcuts.businessService()"/>
</aop:config>

Assume you have an aspect CommonPointcuts.

Declaration a slicer inside an aspect is very similar to a top-level slicer declaration, as shown in the following example:


<aop:config>
    <aop:aspect id="myAspect" ref="aBean">
        <aop:pointcut id="businessService"
            expression="execution(* com.xyz.myapp.service.*.*(..))"/>
        ...
    </aop:aspect>
</aop:config>

Just like @AspectJ, slices declared using the schema-based definition style can collect connection point context. For example, the following slice collects the this object as a junction point context and passes it to the tip:


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

The advice must be declared to obtain the collected connection point context by including parameters with the appropriate names, as shown below:

Java

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

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

When combining slice subexpressions, use &amp;&amp; in an XML document is inconvenient, so instead of &amp;&amp;, || and ! you can use the keywords and, or and not respectively. For example, the previous slice can be written as follows:

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

Note that slicers defined this way refer to their XML id and do not can be used as named slices to compose compound slices. Thus, support for named slices in the schema-based definition style is more limited than in the @AspectJ style.

Declaring Tips

The same five kinds of tips are used to support schema-based AOP. as for the @AspectJ style, and they have exactly the same semantics.

Before Advice

The "before" advice is executed before the corresponding method is executed. It is declared within <aop:aspect> using the <aop:before> element, as shown in the following example:


<aop:aspect id="beforeExample" ref="aBean">
    <aop:before
        pointcut-ref="dataAccessOperation"
        method="doAccessCheck"/>
    ...
</aop:aspect>

Here dataAccessOperation is the id of the slice, defined at the top (<aop:config>) level. To define a slice embedding, replace the pointcut-ref attribute with the pointcut attribute, as shown below:


<aop:aspect id="beforeExample" ref="aBean">
    <aop:before
        pointcut="execution(* com.xyz.myapp.dao.*.*(..))"
        method="doAccessCheck"/>
    ...
</aop:aspect>

As noted when looking at the @AspectJ style, using named slicers can greatly improve the readability of your code.

The method attribute identifies the (doAccessCheck) method that provides the advice body. This method must be defined for the bean referenced by the aspect element containing the tip. Before performing a data access operation (the method execution join point corresponding to the slice expression), the doAccessCheck method is called on the aspected bean.

After Returning Tip

Tip "after returning" is executed when the matched method completes in normal order. It is declared inside <aop:aspect> in the same way as the previous tip. The following example shows how to declare it:

 
<aop:aspect id="afterReturningExample" ref="aBean">
    <aop:after-returning
        pointcut-ref="dataAccessOperation"
        method="doAccessCheck"/>
    ...
</aop:aspect>

As in @AspectJ style, you can get the return value in the body of the advice. To do this, use the returning attribute to specify the name of the parameter to which the return value should be passed, as shown in the following example:


<aop:aspect id="afterReturningExample" ref="aBean">
    <aop:after-returning
        pointcut-ref="dataAccessOperation"
        returning="retVal"
        method="doAccessCheck"/>
    ...
</aop:aspect>

The doAccessCheck method must declare a parameter called retVal. The type of this parameter limits the matching search in the same way as described for @AfterReturning. For example, you cannot declare a method signature as follows:

Java
public void doAccessCheck(Object retVal) {...
Kotlin
fun doAccessCheck(retVal: Any) {...

After Throwing Tip

The "after throwing" tip is executed when the execution of the corresponding method ends with an exception being thrown. It is declared within <aop:aspect> using the after-throwing element, as shown in the following example:


<aop:aspect id="afterThrowingExample" ref="aBean">
    <aop:after-throwing
        pointcut-ref="dataAccessOperation"
        method="doRecoveryActions"/>
    ...
</aop:aspect>

As in @AspectJ style, it is possible to get the thrown exception in the body of the advice. To do this, use the throwing attribute to specify the name of the parameter to which the exception should be passed, as shown in the following example:


<aop:aspect id="afterThrowingExample" ref="aBean">
    <aop:after-throwing
        pointcut-ref="dataAccessOperation"
        throwing="dataAccessEx"
        method="doRecoveryActions"/>
    ...
</aop:aspect>

The doRecoveryActions method must declare a parameter called dataAccessEx. The type of this parameter limits the matching search in the same way as described for @AfterThrowing. For example, a method signature can be declared as follows:

Java
public void doRecoveryActions(DataAccessException dataAccessEx) {...
Kotlin
fun doRecoveryActions(dataAccessEx: DataAccessException) {...

After (Finally) Tip

The After (Finally) tip is executed regardless of how the associated method completes execution. You can declare it using the after element, as shown in the following example:


<aop:aspect id="afterFinallyExample" ref="aBean">
    <aop:after
        pointcut-ref="dataAccessOperation"
        method="doReleaseLock"/>
    ...
</aop:aspect>

Around Advice

The last type of advice is the "instead" advice The "instead" advice replaces the execution of the associated method. It can run before or after a method runs and determine when, how, and even whether the method is run at all. The "instead" advice is often used when you want to separate the state "before" and "after" the execution of a method in a thread-safe manner - for example, as for starting and stopping a timer.

Always use the least influential form of advice that meets your requirements.

Always use the least influential form of advice that meets your requirements. For example, don't use the "instead of" advice if the"before" advice is sufficient for your needs.

You can declare an “instead” advice using the aop:around element. The advice method must declare Object as its return type, and the first parameter of the method must be of type ProceedingJoinPoint. In the body of the council method, you need to call proceed() on ProceedingJoinPoint to run the main method. Calling proceed() without arguments will cause the caller's original arguments to be passed to the base method when it is called. For more advanced use cases, there is an overload of the proceed() method that takes an array of (Object[]) arguments. The values in the array will be used as arguments to the base method when it is called.

The following example shows how to declare an "instead" tip in XML:


<aop:aspect id="aroundExample" ref="aBean">
    <aop:around
        pointcut-ref="businessService"
        method="doBasicProfiling"/>
    ...
</aop:aspect>

The implementation of the doBasicProfiling advice can be exactly the same as in the example for @ AspectJ (minus the annotation, of course), as shown in the following example:

Java

public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
    // start the stopwatch
    Object retVal = pjp.proceed();
    // stop the stopwatch
    return retVal;
}
Kotlin

fun doBasicProfiling(pjp: ProceedingJoinPoint): Any {
    // start the stopwatch
    val retVal = pjp .proceed()
    // stop the stopwatch
    return pjp.proceed()
}

Advice options

Schema-based declaration style is fully supported typed advice in the same way as described for support in the case of @AspectJ - by mapping slice parameters by name to advice method parameters. If you need to explicitly specify argument names for advice methods (without using the detection strategies described earlier), you can do this using the arg-names attribute of the advice element, which is treated the same as the argNames attribute in the advice annotation. The following example shows how to specify the argument name in XML:


<aop:before
        pointcut="com.xyz.lib.Pointcuts.anyPublicMethod() and @annotation(auditable)"
        method="audit"
        arg-names="auditable"/>

The arg-names attribute accepts a comma-separated list of parameter names.

The next, slightly more complex example of the approach, based on XSD, demonstrates some tips used in combination with a number of strongly typed parameters:

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

Next comes the aspect. Note that the profile(..) method takes several strongly typed parameters, the first of which is the connection point used to continue calling the method. The presence of this parameter indicates that profile(..) should be used as a around tip, as shown in the following example:

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

Finally, the following example XML configuration affects the execution of the previous advice for a specific connection point:


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

Consider the following driver script:

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

With this Boot class we would get an execution result similar to the following with standard output:

StopWatch 'Profiling for 'Pengo' and '12': running time (millis) = 0
-----------------------------------------
ms     %     Task name
-----------------------------------------
00000  ?  execution(getFoo)

Ordering Hints

If multiple Hints need to be executed at the same connection point (executing method), the ordering rules are described in section "Ordering advice." Precedence between aspects is determined through the order attribute on the <aop:aspect> element, or by adding the @Order annotation to the bean supporting the aspect, or by implementing a binomial of the Ordered interface.

Unlike the precedence rules for advice methods defined in the same class, annotated with @Aspect, if two advice defined in the same <aop:aspect> element are to be executed at the same join point, precedence is determined by the order in which the advice elements are declared in the enclosing element <aop:aspect> from highest to lowest level of precedence.

For example, if there is advice around and advice before elements defined in the same <aop:aspect> element that are applied to the same join point if you want the around advice to be guaranteed to have more higher level of precedence than the before advice, the <aop:around> element must be declared before the <aop:before> element.

As a rule of thumb, if you find that there are multiple tips defined in a single <aop:aspect> element that apply to the same join point, consider to combine such advice methods into one advice method for each join point in each <aop:aspect> element, or to refactor the advice into separate <aop:aspect> elements, which can be ordered at the aspect level.

Introductions

Introductions (known in AspectJ as intertype declarations) allow an aspect to declare that the objects provided with the advice implement a given interface, and provide an implementation of that interface on behalf of those objects.

You can implement the introduction using the aop:declare-parents element inside the aop:aspect element. You can use the aop:declare-parents element to declare that matching types have a new parent type (hence the name). For example, given the UsageTracked interface and the DefaultUsageTracked implementation of that interface, the next aspect declares that all service interface implementers also implement the UsageTracked interface. (For example, to open statistics via 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>

The class that serves as the basis for the UseTracking bean will contain the following method:

Java

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

fun recordUsage(usageTracked: UsageTracked) {
    usageTracked.incrementUseCount()
} 

The interface that must be implemented is determined by the implement-interface attribute. The value of the types-matching attribute is the AspectJ type template. Any bean of the appropriate type implements the UsageTracked interface. Note that in the "before" tip in the previous example, service beans can be directly used as implementations of the UsageTracked interface. To access the bean programmatically, you can write the following:

Java
UsageTracked usageTracked = (UsageTracked) context.getBean("myService");
Kotlin
val usageTracked = context.getBean("myService") as UsageTracked

Aspect Instancing Models

The only supported instantiation model for schema-defined aspects is the singleton instantiation model. Support for other instantiation models may be added in future releases.

Advisors

The concept of "advisors" comes from the AOP support facilities defined in Spring and has no direct equivalent in AspectJ. An advisor is like a small self-contained aspect that is provided with a single piece of advice. The council itself is represented by a bean and must implement one of the council interfaces. Advisors can take advantage of slice expressions from AspectJ.

Spring supports the concept of an advisor using the <aop:advisor> element. It is most often used in conjunction with transactional advice, which also has its own namespace in Spring. The following example shows the advisor:


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

In addition to the pointcut-ref attribute used in the previous example, you can also use the pointcut to define the cut expression embedding.

To define the seniority of the advisor so that the advisor can participate in ordering, use the order attribute to define the value Ordered advisor.

AOP Schema Example

This section shows what the concurrent lock failure retry example from An AOP Example would look like if it were rewritten using schema support.

Sometimes business services may fail due to concurrency issues (such as deadlock). If the operation is repeated, it will most likely be successful the next time you try. For business services where it is appropriate to retry an operation under these conditions (idempotent operations that do not need to go back to the user to resolve the conflict), we want to retry the operation in a clear way without the client end seeing a PessimisticLockingFailureException. This is a requirement that obviously spans multiple services at the service level and is therefore ideally suited to be implemented through an aspect.

Since we need to repeat the operation, we need to use the "instead" advice so that we can call the proceed method several times. The following listing shows a basic aspect implementation (which is a regular Java class that uses schema support):

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

Note that the aspect implements the Ordered interface so that we can set the precedence level of the aspect higher than the transaction advice (we need , so that each time you retry the transaction will be a new one). The maxRetries and order properties are configured by Spring. The main action happens in the doConcurrentOperation method of the "instead" council. Let's try to continue execution. In the event of an error throwing an exception PessimisticLockingFailureException, we retry if all retry attempts have not been exhausted.

This class is identical to that , which was used in the example for @AspectJ, but with the annotations removed.

The corresponding Spring configuration looks like this:


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

Note that at this point we are assuming that all business services are idempotent. If this is not the case, you can modify the aspect so that it only repeats truly idempotent operations by introducing the Idempotent annotation and using it to annotate the implementation of utility operations, as shown in the following example:

Java

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

@Retention(AnnotationRetention.RUNTIME)
annotation class Idempotent {
    // marker annotation
}

Changing aspect for repeating only idempotent operations involves refining the slice expression so that only @Idempotent operations match, as shown below:


<aop:pointcut id="idempotentOperation"
        expression="execution(* com.xyz.myapp.service. *.*(..)) and
        @annotation(com.xyz.myapp.service.Idempotent)"/>