Setup
This time I will show you how to create a basic project in Spring Boot using AOP, in order to do that you need to install SDKMAN if you are using Linux or Mac, or posh-gvm if you are using Windows. After that, you can easily install:
- Spring Boot
- Gradle
Then execute this command in your terminal.
spring init --dependencies=web --language=java --build=gradle spring-boot-aop
This is the build.gradle file generated:
buildscript {
ext {
springBootVersion = '2.0.5.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group = 'com.jos.dem.springboot.aop'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-web')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
What is AOP?
Apply same logic in several points in your application is called cross cutting concerns, and in Spring we can implement it using AOP, some basics aspects are:
- Advice: Action taken. Different types of advice include “around,” “before”, “after”, “AfterReturning” and “AfterThrowing” advice.
- Pointcut: Where the action should be applied.
- Aspect: Is an advice and pointcut combination
In this example we are going to define an AfterThrowingAdvice which means an action is executed after some exception has been thrown, so first we need to define a general exception in our application.
package com.jos.dem.springboot.aop.exception;
public class DemoException extends RuntimeException {
public DemoException(String message){
super(message);
}
public DemoException(String message, Throwable cause){
super(message, cause);
}
}
DemoException
is a general exception in our application defined to react in the same way to the specific error.
So our advice look like this:
package com.jos.dem.springboot.aop.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;
import org.springframework.stereotype.Component;
import com.jos.dem.springboot.aop.exception.DemoException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Aspect
@Component
public class AfterThrowingAdvice {
private Logger log = LoggerFactory.getLogger(this.getClass());
@AfterThrowing(pointcut = "execution(* com.jos.dem.springboot.aop.service..**.*(..))", throwing = "ex")
public void doRecoveryActions(RuntimeException ex){
log.info("Wrapping exception: " + ex);
throw new DemoException(ex.getMessage(), ex);
}
}
Explanation
- An aspect is defined with the @Aspect annotation
- A bean managed by the spring context is defined with the @Component annotation
- A pointcut is defined as all methods in package
com.jos.dem.jmailer.service
with any parameter - The action defined in the advice is thrown an
DemoException
So if in any service we throw an Exception, actually a RuntimeException
our advice logic is executed.
package com.jos.dem.springboot.aop.service;
public interface DemoService {
void show();
}
DemoService
implementation
package com.jos.dem.springboot.aop.service.impl;
import org.springframework.stereotype.Service;
import com.jos.dem.springboot.aop.service.DemoService;
import com.jos.dem.springboot.aop.exception.DemoException;
@Service
public class DemoServiceImpl implements DemoService {
public void show(){
throw new DemoException("Throwing Demo Exception");
}
}
As you can see at DemoService
in the show()
method we are throwing an DemoException
so our Advice will be called. Here is the DemoController
and a quick view how this controller is calling DemoService
.
package com.jos.dem.springboot.aop.controller;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.beans.factory.annotation.Autowired;
import com.jos.dem.springboot.aop.service.DemoService;
@RestController
public class DemoController {
@Autowired
private DemoService demoService;
@RequestMapping("/")
public String index(){
demoService.show();
return "Hello World!";
}
}
To get this work, you only need to add aop dependency in your build.gradle
compile 'org.springframework.boot:spring-boot-starter-aop'
Finally, let’s run this project executing: gradle bootRun
command and hit the http://localhost:8080/
url, then you should be able to see our after throwing advice aop strategy:
Wrapping exception: com.jos.dem.springboot.aop.exception.DemoException: Throwing Demo Exception
If you prefer Maven instead of Gradle you use this pom.xml
definition:
<?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.jos.dem</groupId>
<artifactId>aop</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-boot-aop</name>
<description>Show How to use Spring Boot Aspect Oriented Programming</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
<relativePath/>
</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-web</artifactId>
</dependency>
<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>
</project>
To browse the code go here, to download the code:
git clone git@github.com:josdem/spring-boot-aop.git
To run the project with Gradle:
gradle bootRun
To run the project with Maven:
mvn spring-boot:run