1. Overview
In this article, we will go through the various pointcut designators (execution, this, target, within, args) supported in the Spring AOP. Let’s look at the basic key terminologies before we move forward.
1.1. Key Terminologies
Join Point: A point during the execution of a program, such as the execution of a method or the handling of an exception. In Spring AOP, it always represents the method execution.
Advice: An Action to be taken before, after, or around a particular join point or method execution. The different advice types include “around,” “before” and “after” advice.
Pointcut in Spring AOP: A predicate that matches join points. It is a set of one or more join points (method executions) for which the Advice executes.
Pointcut expression: An expression language that helps to match the target methods to apply the advice.
2. Pointcut designators
A pointcut expression contains a pointcut designator and a pattern to match the method executions. Let’s see an example to understand this.
@Pointcut("execution(* transfer(..))")In the above example, execution is the pointcut designator whereas “* transfer(..)” is the pattern enclosed within the braces.
2.1. execution AOP
This is the primary pointcut designator and helps to match the method execution join points. See this article Pointcut expressions to know more about various patterns supported by this execution designator.
2.2. within AOP
Within AOP limits matching to join points within certain types.
@Pointcut("within(com.tedblob.model..*") private void withinModelAll() {}The above expression limits the method executions within the
com.tedblob.model
package (type) and its sub-packages.@Pointcut("within(com.tedblob.model.*") private void withinModel() {}This expression limits the method executions within the
com.tedblob.model
package.2.3 @within AOP
@within limits matching to join points or method executions within types that have the given annotation.
@Pointcut("@within(com.tedblob.annotations.Secured)") public void securedClassPointcut() { }This pointcut matches all the methods inside the class annotated with
@Secured
.@Secured public class Security { public String getSecurityAlgorithm() { return algorithm; } public int getSecurityScore() { return securityScore; } }
securedClassPointcut
matches all the methods(getSecurityAlgorithm, getSecurityScore)
inside thisSecurity
class.2.4. args AOP
Args limits matching to join points or method executions where the arguments are instances of the given types.
args(java.io.Serializable)This expression matches the methods with a single parameter and where the argument passed at runtime is Serializable.
Now, let’s see the difference between execution and args.
execution(* *(java.io.Serializable))Here, the execution expression matches any method with a single parameter of type Serializable class (doesn’t support sub-classes that implement Serializable) whereas args matches if the argument passed at runtime is Serializable. So args can accept a class that implements Serializable class (sub-class) or Serializable class.
2.5. @args AOP
@args
limits matching to join points or method executions where the runtime type of the actual arguments passed have annotations of the provided type(s). Let’s see an example to understand this.
CountryCode
is an enum annotated with custom annotation@Country
. The mentionedcountryPointcut
with@args
matches the method executions with a single argument having annotation@Country
.
getCountryCode
method has a single argumentCountryCode
annotated with@Country
. SocountryPointcut
matches thegetCountryCode
method execution and will execute after that.@Pointcut("@args(com.tedblob.annotations.Country)") public void countryPointcut() { } @After("countryPointcut()") public void afterCountryAdvice(JoinPoint joinPoint) { System.out.println("After - countryPointcut" + joinPoint.getSignature().getName()); }@Retention(RUNTIME) public @interface Country { } @Country public enum CountryCode { US, UK, IN, JA }public String getCountryCode(CountryCode countryCode) { return countryCode; }2.6 @annotation AOP
@annotation
limits matching to method executions where the method has the given annotation.@Pointcut("@annotation(com.tedblob.annotations.Validation)") public void validationPointcut() { }@Validation public boolean validateInput(String input) { return false; }The above
validationPointcut
matches the methods which have the annotation@Validation
. So,validateInput
method will be matched.2.7 this and target
this limits matching to join points (the method executions) where the bean reference (Spring AOP proxy) is an instance of the given type.
target limits matching to join points (the method executions) where the target object (application object being proxied) is an instance of the given type.
Let’s recall the JDK dynamic proxy and CGLIB proxy mechanisms to get a grasp of these concepts.
2.7.1. this and target for JDK Dynamic proxy
The Spring AOP is proxy-based. Consider the target class implements at least one interface, then Spring uses JDK dynamic proxy to create a proxy object for the target class. The proxy object extends the same interface and proxies all the interface methods. The calls to the overridden methods of the target class will go to the proxy object first and the proxy object will delegate to all the advice declared for the target class.
The below target class
CountriesRepositoryImpl
implements an interface. Spring uses Jdk dynamic proxy here and creates a proxy object which also implements the same interface.@Component public class CountriesRepositoryImpl implements CountryRepository { @Override public List<String> getCountries() { return countries; } }package com.tedbob.aspects; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Component @Aspect public class CurrenciesRepositoryAspect { @Before("target(com.tedbob.bls.CountryRepository)") public void beforeTargetCountriesRepository() { System.out.println("beforeCountriesRepository"); } @After("this(com.tedbob.bls.CountryRepository)") public void afterCountriesRepository() { System.out.println("afterCountriesRepository"); } }
Target
designator matches the target object which is of typecom.tedblob.interface.CountryRepository
. Here, thebeforeTargetCountriesRepository
advice matches the target classCountriesRepositoryImpl
.
This
designator matches the proxy object which is of typecom.tedblob.interface.CountryRepository
. Since the proxy object ofCountryRepositoryImpl
also implements the same interfaceCountryRepository
, this advice executes.2.7.1. this and target for CGLIB proxy
You can use @EnableAspectJAutoProxy(proxyTargetClass=true) annotation in your class to prevent the Jdk dynamic proxy. Also, if your target class doesn’t implement any interface, then Spring uses CGLIB proxy and creates a proxy object that will be a subclass of your target class.
@Component public class CountriesRepositoryImpl { public List<String> getCountries() { return countries; } }Here,
this
designator matches the proxy object of typeCountriesRepositoryImpl
. As the proxy object is a subclass ofCountriesRepositoryImpl
, this advice executes whenever any call happens to theCountriesRepositoryImpl
.@Before("his(com.tedblob.interface.CountriesRepositoryImpl)") public void beforeThisCountriesRepository() { System.out.println("beforeThisCountriesRepository"); }The below target designator won’t work in this scenario as the target object doesn’t implement any interface and not instance of any other type. “Exception in thread “main” org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type ‘com.tedbob.interface.CountryRepository’ available” compilation error thrown.
@Before("target(com.tedblob.interface.CountryRepository)") public void beforeTargetCountriesRepository() { System.out.println("beforeCountriesRepository"); }Consider the below scenario where the target class has the annotation @EnableAspectJAutoProxy(proxyTargetClass=true) to use CGLIB proxy and implements an interface. The target designator works here because the target object is of type CountryRepository.
@Component @EnableAspectJAutoProxy(proxyTargetClass=true) public class CountriesRepositoryImpl implements CountryRepository { public int getCountriesCount() { return 0; } } package com.tedbob.aspects; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Component @Aspect public class CurrenciesRepositoryAspect { @Before("target(com.tedbob.bls.CountryRepository)") public void beforeTargetCountriesRepository() { System.out.println("beforeCountriesRepository"); } @After("this(com.tedbob.bls.CountriesRepositoryImpl)") public void afterCountriesRepository() { System.out.println("afterCountriesRepository"); } @Around("this(com.tedbob.bls.CountryRepository)") public int aroundCountriesRepository(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("aroundCountriesRepository"); return (int) joinPoint.proceed(); } }3. Conclusion
In this article, we have seen various pointcut designators and their use cases.
Pingback: Pointcut in Spring AOP - TedBlob
Pingback: Pointcut expression in Spring AOP (various pointcut patterns) - TedBlob
Pingback: What is the JoinPoint argument used for? - TedBlob