Let's play and learn more about Spring AOP
- Step 01 - Setting up AOP Example - Part 1
- Step 02 - Setting up AOP Example - Part 2
- Step 03 - Defining an @Before advice
- Step 04 - Understand AOP Terminology - Pointcut, Advice, Aspect, Join Point, Weaving and Weaver
- Step 05 - Using @After, @AfterReturning, @AfterThrowing advices
- Step 06 - Using @Around advice to implement performance tracing
- Step 07 - Best Practice : Use common Pointcut Configuration
- Step 08 - Quick summary of other Pointcuts
- Step 09 - Creating Custom Annotation and an Aspect for Tracking Time
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.in28minutes.spring.aop</groupId>
<artifactId>spring-aop</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-aop</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>
package com.in28minutes.spring.aop.springaop.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
//AOP
//Configuration
@Aspect
@Configuration
public class AfterAopAspect {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@AfterReturning(value = "com.in28minutes.spring.aop.springaop.aspect.CommonJoinPointConfig.businessLayerExecution()",
returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result) {
logger.info("{} returned with value {}", joinPoint, result);
}
@After(value = "com.in28minutes.spring.aop.springaop.aspect.CommonJoinPointConfig.businessLayerExecution()")
public void after(JoinPoint joinPoint) {
logger.info("after execution of {}", joinPoint);
}
}
package com.in28minutes.spring.aop.springaop.aspect;
import org.aspectj.lang.annotation.Pointcut;
public class CommonJoinPointConfig {
@Pointcut("execution(* com.in28minutes.spring.aop.springaop.data.*.*(..))")
public void dataLayerExecution(){}
@Pointcut("execution(* com.in28minutes.spring.aop.springaop.business.*.*(..))")
public void businessLayerExecution(){}
@Pointcut("dataLayerExecution() && businessLayerExecution()")
public void allLayerExecution(){}
@Pointcut("bean(*dao*)")
public void beanContainingDao(){}
@Pointcut("within(com.in28minutes.spring.aop.springaop.data..*)")
public void dataLayerExecutionWithWithin(){}
@Pointcut("@annotation(com.in28minutes.spring.aop.springaop.aspect.TrackTime)")
public void trackTimeAnnotation(){}
}
package com.in28minutes.spring.aop.springaop.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
@Aspect
@Configuration
public class MethodExecutionCalculationAspect {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Around("com.in28minutes.spring.aop.springaop.aspect.CommonJoinPointConfig.trackTimeAnnotation()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
joinPoint.proceed();
long timeTaken = System.currentTimeMillis() - startTime;
logger.info("Time Taken by {} is {}", joinPoint, timeTaken);
}
}
package com.in28minutes.spring.aop.springaop.aspect;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TrackTime {
}
package com.in28minutes.spring.aop.springaop.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
//AOP
//Configuration
@Aspect
@Configuration
public class UserAccessAspect {
private Logger logger = LoggerFactory.getLogger(this.getClass());
//What kind of method calls I would intercept
//execution(* PACKAGE.*.*(..))
//Weaving & Weaver
@Before("com.in28minutes.spring.aop.springaop.aspect.CommonJoinPointConfig.dataLayerExecution()")
public void before(JoinPoint joinPoint){
//Advice
logger.info(" Check for user access ");
logger.info(" Allowed execution for {}", joinPoint);
}
}
package com.in28minutes.spring.aop.springaop.business;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.in28minutes.spring.aop.springaop.aspect.TrackTime;
import com.in28minutes.spring.aop.springaop.data.Dao1;
@Service
public class Business1 {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private Dao1 dao1;
@TrackTime
public String calculateSomething(){
//Business Logic
String value = dao1.retrieveSomething();
logger.info("In Business - {}", value);
return value;
}
}
package com.in28minutes.spring.aop.springaop.business;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.in28minutes.spring.aop.springaop.data.Dao2;
@Service
public class Business2 {
@Autowired
private Dao2 dao2;
public String calculateSomething(){
//Business Logic
return dao2.retrieveSomething();
}
}
package com.in28minutes.spring.aop.springaop.data;
import org.springframework.stereotype.Repository;
import com.in28minutes.spring.aop.springaop.aspect.TrackTime;
@Repository
public class Dao1 {
@TrackTime
public String retrieveSomething(){
return "Dao1";
}
}
package com.in28minutes.spring.aop.springaop.data;
import org.springframework.stereotype.Repository;
@Repository
public class Dao2 {
public String retrieveSomething(){
return "Dao2";
}
}
package com.in28minutes.spring.aop.springaop;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.in28minutes.spring.aop.springaop.business.Business1;
import com.in28minutes.spring.aop.springaop.business.Business2;
@SpringBootApplication
public class SpringAopApplication implements CommandLineRunner{
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private Business1 business1;
@Autowired
private Business2 business2;
public static void main(String[] args) {
SpringApplication.run(SpringAopApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
logger.info(business1.calculateSomething());
logger.info(business2.calculateSomething());
}
}
package com.in28minutes.spring.aop.springaop;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringAopApplicationTests {
@Test
public void contextLoads() {
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.in28minutes.spring.aop</groupId>
<artifactId>spring-aop</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-aop</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>
package com.in28minutes.spring.aop.springaop.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
//AOP
//Configuration
@Aspect
@Configuration
public class AfterAopAspect {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@AfterReturning(value = "com.in28minutes.spring.aop.springaop.aspect.CommonJoinPointConfig.businessLayerExecution()",
returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result) {
logger.info("{} returned with value {}", joinPoint, result);
}
@After(value = "com.in28minutes.spring.aop.springaop.aspect.CommonJoinPointConfig.businessLayerExecution()")
public void after(JoinPoint joinPoint) {
logger.info("after execution of {}", joinPoint);
}
}
package com.in28minutes.spring.aop.springaop.aspect;
import org.aspectj.lang.annotation.Pointcut;
public class CommonJoinPointConfig {
@Pointcut("execution(* com.in28minutes.spring.aop.springaop.data.*.*(..))")
public void dataLayerExecution(){}
@Pointcut("execution(* com.in28minutes.spring.aop.springaop.business.*.*(..))")
public void businessLayerExecution(){}
@Pointcut("dataLayerExecution() && businessLayerExecution()")
public void allLayerExecution(){}
@Pointcut("bean(*dao*)")
public void beanContainingDao(){}
@Pointcut("within(com.in28minutes.spring.aop.springaop.data..*)")
public void dataLayerExecutionWithWithin(){}
@Pointcut("@annotation(com.in28minutes.spring.aop.springaop.aspect.TrackTime)")
public void trackTimeAnnotation(){}
}
package com.in28minutes.spring.aop.springaop.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
@Aspect
@Configuration
public class MethodExecutionCalculationAspect {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Around("com.in28minutes.spring.aop.springaop.aspect.CommonJoinPointConfig.trackTimeAnnotation()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
joinPoint.proceed();
long timeTaken = System.currentTimeMillis() - startTime;
logger.info("Time Taken by {} is {}", joinPoint, timeTaken);
}
}
package com.in28minutes.spring.aop.springaop.aspect;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TrackTime {
}
package com.in28minutes.spring.aop.springaop.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
//AOP
//Configuration
@Aspect
@Configuration
public class UserAccessAspect {
private Logger logger = LoggerFactory.getLogger(this.getClass());
//What kind of method calls I would intercept
//execution(* PACKAGE.*.*(..))
//Weaving & Weaver
@Before("com.in28minutes.spring.aop.springaop.aspect.CommonJoinPointConfig.dataLayerExecution()")
public void before(JoinPoint joinPoint){
//Advice
logger.info(" Check for user access ");
logger.info(" Allowed execution for {}", joinPoint);
}
}
package com.in28minutes.spring.aop.springaop.business;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.in28minutes.spring.aop.springaop.aspect.TrackTime;
import com.in28minutes.spring.aop.springaop.data.Dao1;
@Service
public class Business1 {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private Dao1 dao1;
@TrackTime
public String calculateSomething(){
//Business Logic
String value = dao1.retrieveSomething();
logger.info("In Business - {}", value);
return value;
}
}
package com.in28minutes.spring.aop.springaop.business;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.in28minutes.spring.aop.springaop.data.Dao2;
@Service
public class Business2 {
@Autowired
private Dao2 dao2;
public String calculateSomething(){
//Business Logic
return dao2.retrieveSomething();
}
}
package com.in28minutes.spring.aop.springaop.data;
import org.springframework.stereotype.Repository;
import com.in28minutes.spring.aop.springaop.aspect.TrackTime;
@Repository
public class Dao1 {
@TrackTime
public String retrieveSomething(){
return "Dao1";
}
}
package com.in28minutes.spring.aop.springaop.data;
import org.springframework.stereotype.Repository;
@Repository
public class Dao2 {
public String retrieveSomething(){
return "Dao2";
}
}
package com.in28minutes.spring.aop.springaop;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.in28minutes.spring.aop.springaop.business.Business1;
import com.in28minutes.spring.aop.springaop.business.Business2;
@SpringBootApplication
public class SpringAopApplication implements CommandLineRunner{
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private Business1 business1;
@Autowired
private Business2 business2;
public static void main(String[] args) {
SpringApplication.run(SpringAopApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
logger.info(business1.calculateSomething());
logger.info(business2.calculateSomething());
}
}
package com.in28minutes.spring.aop.springaop;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringAopApplicationTests {
@Test
public void contextLoads() {
}
}