[[aop-api-advice]]
= Advice API in Spring

Now we can examine how Spring AOP handles advice.


[[aop-api-advice-lifecycle]]
== Advice Lifecycles

Each advice is a Spring bean. An advice instance can be shared across all advised
objects or be unique to each advised object. This corresponds to per-class or
per-instance advice.

Per-class advice is used most often. It is appropriate for generic advice, such as
transaction advisors. These do not depend on the state of the proxied object or add new
state. They merely act on the method and arguments.

Per-instance advice is appropriate for introductions, to support mixins. In this case,
the advice adds state to the proxied object.

You can use a mix of shared and per-instance advice in the same AOP proxy.


[[aop-api-advice-types]]
== Advice Types in Spring

Spring provides several advice types and is extensible to support
arbitrary advice types. This section describes the basic concepts and standard advice types.

[[aop-api-advice-around]]
=== Interception Around Advice

The most fundamental advice type in Spring is _interception around advice_.

Spring is compliant with the AOP Alliance interface for around advice that uses method
interception. Classes that implement around advice should therefore implement the
following `MethodInterceptor` interface from the `org.aopalliance.intercept` package:

[source,java,indent=0,subs="verbatim,quotes"]
----
	public interface MethodInterceptor extends Interceptor {

		Object invoke(MethodInvocation invocation) throws Throwable;
	}
----

The `MethodInvocation` argument to the `invoke()` method exposes the method being
invoked, the target join point, the AOP proxy, and the arguments to the method. The
`invoke()` method should return the invocation's result: typically the return value of
the join point.

The following example shows a simple `MethodInterceptor` implementation:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	public class DebugInterceptor implements MethodInterceptor {

		public Object invoke(MethodInvocation invocation) throws Throwable {
			System.out.println("Before: invocation=[" + invocation + "]");
			Object result = invocation.proceed();
			System.out.println("Invocation returned");
			return result;
		}
	}
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	class DebugInterceptor : MethodInterceptor {

		override fun invoke(invocation: MethodInvocation): Any {
			println("Before: invocation=[$invocation]")
			val result = invocation.proceed()
			println("Invocation returned")
			return result
		}
	}
----
======

Note the call to the `proceed()` method of `MethodInvocation`. This proceeds down the
interceptor chain towards the join point. Most interceptors invoke this method and
return its return value. However, a `MethodInterceptor`, like any around advice, can
return a different value or throw an exception rather than invoke the proceed method.
However, you do not want to do this without good reason.

NOTE: `MethodInterceptor` implementations offer interoperability with other AOP Alliance-compliant AOP
implementations. The other advice types discussed in the remainder of this section
implement common AOP concepts but in a Spring-specific way. While there is an advantage
in using the most specific advice type, stick with `MethodInterceptor` around advice if
you are likely to want to run the aspect in another AOP framework. Note that pointcuts
are not currently interoperable between frameworks, and the AOP Alliance does not
currently define pointcut interfaces.

[[aop-api-advice-before]]
=== Before Advice

A simpler advice type is a _before advice_. This does not need a `MethodInvocation`
object, since it is called only before entering the method.

The main advantage of a before advice is that there is no need to invoke the `proceed()`
method and, therefore, no possibility of inadvertently failing to proceed down the
interceptor chain.

The following listing shows the `MethodBeforeAdvice` interface:

[source,java,indent=0,subs="verbatim,quotes"]
----
	public interface MethodBeforeAdvice extends BeforeAdvice {

		void before(Method m, Object[] args, Object target) throws Throwable;
	}
----

Note that the return type is `void`. Before advice can insert custom behavior before the join
point runs but cannot change the return value. If a before advice throws an
exception, it stops further execution of the interceptor chain. The exception
propagates back up the interceptor chain. If it is unchecked or on the signature of
the invoked method, it is passed directly to the client. Otherwise, it is
wrapped in an unchecked exception by the AOP proxy.

The following example shows a before advice in Spring, which counts all method invocations:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	public class CountingBeforeAdvice implements MethodBeforeAdvice {

		private int count;

		public void before(Method m, Object[] args, Object target) throws Throwable {
			++count;
		}

		public int getCount() {
			return count;
		}
	}
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	class CountingBeforeAdvice : MethodBeforeAdvice {

		var count: Int = 0

		override fun before(m: Method, args: Array<Any>, target: Any?) {
			++count
		}
	}
----
======

TIP: Before advice can be used with any pointcut.

[[aop-api-advice-throws]]
=== Throws Advice

_Throws advice_ is invoked after the return of the join point if the join point threw
an exception. Spring offers typed throws advice. Note that this means that the
`org.springframework.aop.ThrowsAdvice` interface does not contain any methods. It is a
marker interface identifying that the given object implements one or more typed throws
advice methods. These should be in the following form:

[source,java,indent=0,subs="verbatim,quotes"]
----
	afterThrowing([Method, args, target], subclassOfThrowable)
----

Only the last argument is required. The method signatures may have either one or four
arguments, depending on whether the advice method is interested in the method and
arguments. The next two listings show classes that are examples of throws advice.

The following advice is invoked if a `RemoteException` is thrown (including subclasses of
`RemoteException`):

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	public class RemoteThrowsAdvice implements ThrowsAdvice {

		public void afterThrowing(RemoteException ex) throws Throwable {
			// Do something with remote exception
		}
	}
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	class RemoteThrowsAdvice : ThrowsAdvice {

		fun afterThrowing(ex: RemoteException) {
			// Do something with remote exception
		}
	}
----
======

Unlike the preceding advice, the next example declares four arguments, so that it has
access to the invoked method, method arguments, and target object. The following advice
is invoked if a `ServletException` is thrown:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	public class ServletThrowsAdviceWithArguments implements ThrowsAdvice {

		public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {
			// Do something with all arguments
		}
	}
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	class ServletThrowsAdviceWithArguments : ThrowsAdvice {

		fun afterThrowing(m: Method, args: Array<Any>, target: Any, ex: ServletException) {
			// Do something with all arguments
		}
	}
----
======

The final example illustrates how these two methods could be used in a single class
that handles both `RemoteException` and `ServletException`. Any number of throws advice
methods can be combined in a single class. The following listing shows the final example:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	public static class CombinedThrowsAdvice implements ThrowsAdvice {

		public void afterThrowing(RemoteException ex) throws Throwable {
			// Do something with remote exception
		}

		public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {
			// Do something with all arguments
		}
	}
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	class CombinedThrowsAdvice : ThrowsAdvice {

		fun afterThrowing(ex: RemoteException) {
			// Do something with remote exception
		}

		fun afterThrowing(m: Method, args: Array<Any>, target: Any, ex: ServletException) {
			// Do something with all arguments
		}
	}
----
======

NOTE: If a throws-advice method throws an exception itself, it overrides the
original exception (that is, it changes the exception thrown to the user). The overriding
exception is typically a RuntimeException, which is compatible with any method
signature. However, if a throws-advice method throws a checked exception, it must
match the declared exceptions of the target method and is, hence, to some degree
coupled to specific target method signatures. _Do not throw an undeclared checked
exception that is incompatible with the target method's signature!_

TIP: Throws advice can be used with any pointcut.

[[aop-api-advice-after-returning]]
=== After Returning Advice

An _after returning advice_ in Spring must implement the
`org.springframework.aop.AfterReturningAdvice` interface, which the following listing shows:

[source,java,indent=0,subs="verbatim,quotes"]
----
	public interface AfterReturningAdvice extends Advice {

		void afterReturning(Object returnValue, Method m, Object[] args, Object target)
				throws Throwable;
	}
----

An after returning advice has access to the return value (which it cannot modify),
the invoked method, the method's arguments, and the target.

The following after returning advice counts all successful method invocations that have
not thrown exceptions:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	public class CountingAfterReturningAdvice implements AfterReturningAdvice {

		private int count;

		public void afterReturning(Object returnValue, Method m, Object[] args, Object target)
				throws Throwable {
			++count;
		}

		public int getCount() {
			return count;
		}
	}
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	class CountingAfterReturningAdvice : AfterReturningAdvice {

		var count: Int = 0
			private set

		override fun afterReturning(returnValue: Any?, m: Method, args: Array<Any>, target: Any?) {
			++count
		}
	}
----
======

This advice does not change the execution path. If it throws an exception, it is
thrown up the interceptor chain instead of the return value.

TIP: After returning advice can be used with any pointcut.

[[aop-api-advice-introduction]]
=== Introduction Advice

Spring treats _introduction advice_ as a special kind of interception advice.

Introduction requires an `IntroductionAdvisor` and an `IntroductionInterceptor` that
implement the following interface:

[source,java,indent=0,subs="verbatim,quotes"]
----
	public interface IntroductionInterceptor extends MethodInterceptor {

		boolean implementsInterface(Class intf);
	}
----

The `invoke()` method inherited from the AOP Alliance `MethodInterceptor` interface must
implement the introduction. That is, if the invoked method is on an introduced
interface, the introduction interceptor is responsible for handling the method call -- it
cannot invoke `proceed()`.

Introduction advice cannot be used with any pointcut, as it applies only at the class,
rather than the method, level. You can only use introduction advice with the
`IntroductionAdvisor`, which has the following methods:

[source,java,indent=0,subs="verbatim,quotes"]
----
	public interface IntroductionAdvisor extends Advisor, IntroductionInfo {

		ClassFilter getClassFilter();

		void validateInterfaces() throws IllegalArgumentException;
	}

	public interface IntroductionInfo {

		Class<?>[] getInterfaces();
	}
----

There is no `MethodMatcher` and, hence, no `Pointcut` associated with introduction
advice. Only class filtering is logical.

The `getInterfaces()` method returns the interfaces introduced by this advisor.

The `validateInterfaces()` method is used internally to see whether or not the
introduced interfaces can be implemented by the configured `IntroductionInterceptor`.

Consider an example from the Spring test suite and suppose we want to
introduce the following interface to one or more objects:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	public interface Lockable {
		void lock();
		void unlock();
		boolean locked();
	}
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	interface Lockable {
		fun lock()
		fun unlock()
		fun locked(): Boolean
	}
----
======

This illustrates a mixin. We want to be able to cast advised objects to `Lockable`,
whatever their type and call lock and unlock methods. If we call the `lock()` method, we
want all setter methods to throw a `LockedException`. Thus, we can add an aspect that
provides the ability to make objects immutable without them having any knowledge of it:
a good example of AOP.

First, we need an `IntroductionInterceptor` that does the heavy lifting. In this
case, we extend the `org.springframework.aop.support.DelegatingIntroductionInterceptor`
convenience class. We could implement `IntroductionInterceptor` directly, but using
`DelegatingIntroductionInterceptor` is best for most cases.

The `DelegatingIntroductionInterceptor` is designed to delegate an introduction to an
actual implementation of the introduced interfaces, concealing the use of interception
to do so. You can set the delegate to any object using a constructor argument. The
default delegate (when the no-argument constructor is used) is `this`. Thus, in the next example,
the delegate is the `LockMixin` subclass of `DelegatingIntroductionInterceptor`.
Given a delegate (by default, itself), a `DelegatingIntroductionInterceptor` instance
looks for all interfaces implemented by the delegate (other than
`IntroductionInterceptor`) and supports introductions against any of them.
Subclasses such as `LockMixin` can call the `suppressInterface(Class intf)`
method to suppress interfaces that should not be exposed. However, no matter how many
interfaces an `IntroductionInterceptor` is prepared to support, the
`IntroductionAdvisor` used controls which interfaces are actually exposed. An
introduced interface conceals any implementation of the same interface by the target.

Thus, `LockMixin` extends `DelegatingIntroductionInterceptor` and implements `Lockable`
itself. The superclass automatically picks up that `Lockable` can be supported for
introduction, so we do not need to specify that. We could introduce any number of
interfaces in this way.

Note the use of the `locked` instance variable. This effectively adds additional state
to that held in the target object.

The following example shows the example `LockMixin` class:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	public class LockMixin extends DelegatingIntroductionInterceptor implements Lockable {

		private boolean locked;

		public void lock() {
			this.locked = true;
		}

		public void unlock() {
			this.locked = false;
		}

		public boolean locked() {
			return this.locked;
		}

		public Object invoke(MethodInvocation invocation) throws Throwable {
			if (locked() && invocation.getMethod().getName().indexOf("set") == 0) {
				throw new LockedException();
			}
			return super.invoke(invocation);
		}
	}
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	class LockMixin : DelegatingIntroductionInterceptor(), Lockable {

		private var locked: Boolean = false

		fun lock() {
			this.locked = true
		}

		fun unlock() {
			this.locked = false
		}

		fun locked(): Boolean {
			return this.locked
		}

		override fun invoke(invocation: MethodInvocation): Any? {
			if (locked() && invocation.method.name.indexOf("set") == 0) {
				throw LockedException()
			}
			return super.invoke(invocation)
		}
	}
----
======

Often, you need not override the `invoke()` method. The
`DelegatingIntroductionInterceptor` implementation (which calls the `delegate` method if
the method is introduced, otherwise proceeds towards the join point) usually
suffices. In the present case, we need to add a check: no setter method can be invoked
if in locked mode.

The required introduction only needs to hold a distinct
`LockMixin` instance and specify the introduced interfaces (in this case, only
`Lockable`). A more complex example might take a reference to the introduction
interceptor (which would be defined as a prototype). In this case, there is no
configuration relevant for a `LockMixin`, so we create it by using `new`.
The following example shows our `LockMixinAdvisor` class:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	public class LockMixinAdvisor extends DefaultIntroductionAdvisor {

		public LockMixinAdvisor() {
			super(new LockMixin(), Lockable.class);
		}
	}
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	class LockMixinAdvisor : DefaultIntroductionAdvisor(LockMixin(), Lockable::class.java)
----
======

We can apply this advisor very simply, because it requires no configuration. (However, it
is impossible to use an `IntroductionInterceptor` without an
`IntroductionAdvisor`.) As usual with introductions, the advisor must be per-instance,
as it is stateful. We need a different instance of `LockMixinAdvisor`, and hence
`LockMixin`, for each advised object. The advisor comprises part of the advised object's
state.

We can apply this advisor programmatically by using the `Advised.addAdvisor()` method or
(the recommended way) in XML configuration, as any other advisor. All proxy creation
choices discussed below, including "`auto proxy creators,`" correctly handle introductions
and stateful mixins.
