Professional Documents
Culture Documents
10 Spring Boot 3 Aop
10 Spring Boot 3 Aop
10 Spring Boot 3 Aop
Overview
© luv2code LLC
Application Architecture
entityManager.persist(theAccount);
}
entityManager.persist(theAccount);
entityManager.persist(theAccount);
entityManager.persist(theAccount);
entityManager.persist(theAccount);
entityManager.persist(theAccount);
• Code Scattering
• Can all classes extends from your base class? … plus no multiple inheritance
• Delegation?
Cross-Cutting Concerns
S S S
L L L
E E E
O Product O Product O Product
C C C
G Controller G Service G DAO
U U U
G G G
R R R
I I I
I I I
N N N
T T T
G Blog G Blog G Blog
Y Y Y
Controller Service DAO
fi
Account Account Account
Controller Service DAO
MainApp TargetObject
fi
• Much better than being scattered everywhere
• Con gurable
fi
• Based on con guration, apply Aspects selectively to different parts of app
fi
• No need to make changes to main application code … very important!
• Audit logging
• Exception handling
• API Management
• analytics: what are peak times? what is average load? who is top user?
fl
follow
• Resolve code scatter
• Minor performance
• Applied selectively based cost for aspect
on con guration execution
fi
(run-time weaving)
© luv2code LLC
AOP Terminology
• Aspect: module of code for a cross-cutting concern (logging, security, …)
• www.eclipse.org/aspectj
fi
• code weaving: compile-time, post compile-time and load-time
• AspectJ supports
fi
• weaving: compile-time, post compile-time and load-time
• My recommendation
• AspectJ in Action
• by Raminvas Laddad
www.luv2code.com
@Before Advice - Interaction
MainApp TargetObject
www.luv2code.com
Advice - Interaction
@Before
www.luv2code.com
Advice - Interaction
@Before
@AfterReturning
www.luv2code.com
@Before Advice - Use Cases
• Most common
• Audit logging
• API Management
• analytics: what are peak times? what is average load? who is top user?
www.luv2code.com
AOP Example - Overview Step-
By-S
tep
Target
Main AOP Logging Object
App Proxy Aspect
AccountDAO
www.luv2code.com
Spring Boot AOP Starter
• Add the dependency for Spring Boot AOP Starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
www.luv2code.com
Development Process - @Before Step-
By-S
tep
www.luv2code.com
Step 1: Create Target Object: AccountDAO
public interface AccountDAO {
void addAccount() {
}
@Component
public class AccountDAOImpl implements AccountDAO {
}
}
www.luv2code.com
Step 2: Create main app
@SpringBootApplication
public class AopdemoApplication {
@Bean
public CommandLineRunner commandLineRunner(AccountDAO theAccountDAO) {
demoTheBeforeAdvice(theAccountDAO);
};
}
www.luv2code.com
Step 3: Create an Aspect with @Before advice
@Aspect
@Component
public class MyDemoLoggingAspect {
...
www.luv2code.com
Step 3: Create an Aspect with @Before advice
@Aspect
@Component
public class MyDemoLoggingAspect {
...
www.luv2code.com
Step 3: Create an Aspect with @Before advice
@Aspect
@Component
public class MyDemoLoggingAspect {
www.luv2code.com
Best Practices: Aspect and Advices
www.luv2code.com
AOP - Pointcut Expressions
I made a promise to you …
www.luv2code.com
AOP Terminology
www.luv2code.com
Pointcut Expression Language
• Spring AOP uses AspectJ’s pointcut expression language
www.luv2code.com
Match on Method Name
www.luv2code.com
Pointcut Expression Language
www.luv2code.com
Pointcut Expression Examples
Match on method names
Return
Modifier type
Declaring Method
www.luv2code.com
Pointcut Expression Examples
Match on method names
Return
Modifier
type
Method
www.luv2code.com
Pointcut Expression Examples
Match on method names (using wildcards)
Return
Modifier type
Method
www.luv2code.com
Pointcut Expression Examples
Match on method names (using wildcards)
Return
Modifier type
fi
Method
www.luv2code.com
Pointcut Expression Examples
• Use wildcards on return type
Return
type
@Before(“execution(public * processCreditCard*())”)
Method
www.luv2code.com
Pointcut Expression Examples
• Modi er is optional … so you don’t have to list it
fi
Return
type
@Before(“execution(* processCreditCard*())”)
Method
www.luv2code.com
AOP - Pointcut Expressions (cont)
Match on Parameters
www.luv2code.com
Parameter Pattern Wildcards
• For param-pattern
www.luv2code.com
Pointcut Expression Examples
Match on method parameters
www.luv2code.com
Pointcut Expression Examples
Match on method parameters
Return Param
type no-args
@Before("execution(* addAccount())")
Method
www.luv2code.com
Pointcut Expression Examples
Match on method parameters
@Before("execution(* addAccount(com.luv2code.aopdemo.Account))")
Method
www.luv2code.com
Pointcut Expression Examples
Match on method parameters (using wildcards)
www.luv2code.com
Pointcut Expression Examples
Match on method parameters (using wildcards)
Return Param
type Type
@Before("execution(* addAccount(..))”)
Method
www.luv2code.com
Match on Package
www.luv2code.com
Package - Pointcut Expression Examples
Match on methods in a package
@Before("execution(* com.luv2code.aopdemo.dao.*.*(..))")
Class Method
www.luv2code.com
If you are using IntelliJ Ultimate
You may encounter this error
Exception encountered during context initialization - cancelling
refresh attempt:
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'mbeanExporter' defined in class path
resource [org/springframework/boot/autoconfigure/jmx/
JmxAutoConfiguration.class]
© luv2code LLC
Why?
© luv2code LLC
When using wildcards with AOP,
caution should be taken.
Recommendation is to:
- narrow your pointcut expressions
- limit them to your project package
© luv2code LLC
In this case, our pointcut expression is too broad.
@Before("execution(* com.luv2code..add*(..))")
© luv2code LLC
Aspect-Oriented Programming (AOP)
Pointcut Declarations
Problem
• How can we reuse a pointcut expression?
@Before("execution(* com.luv2code.aopdemo.dao.*.*(..))")
public void beforeAddAccountAdvice() {
...
}
www.luv2code.com
Possible Solution
• Could just do the old copy/paste method … yuk!
@Before("execution(* com.luv2code.aopdemo.dao.*.*(..))")
public void beforeAddAccountAdvice() {
...
}
www.luv2code.com
Possible Solution
• Could just do the old copy/paste method … yuk!
@Before("execution(* com.luv2code.aopdemo.dao.*.*(..))")
public void beforeAddAccountAdvice() {
...
}
@Before("execution(* com.luv2code.aopdemo.dao.*.*(..))")
public void performApiAnalytics() {
...
}
www.luv2code.com
Ideal Solution
www.luv2code.com
Development Process Step-
By-S
tep
www.luv2code.com
Step 1- Create Pointcut Declaration
• Create a pointcut declaration once
@Pointcut("execution(* com.luv2code.aopdemo.dao.*.*(..))")
private void forDaoPackage() {}
www.luv2code.com
Step 1- Create Pointcut Declaration
@Aspect
@Component
public class MyDemoLoggingAspect {
www.luv2code.com
Step 1- Create Pointcut Declaration
@Aspect
@Component
public class MyDemoLoggingAspect {
@Pointcut("execution(* com.luv2code.aopdemo.dao.*.*(..))")
private void forDaoPackage() {}
www.luv2code.com
Step 2 - Apply to Multiple Advices
@Aspect
@Component
public class MyDemoLoggingAspect {
@Pointcut("execution(* com.luv2code.aopdemo.dao.*.*(..))")
private void forDaoPackage() {}
@Before("forDaoPackage()")
public void beforeAddAccountAdvice() {
…
}
www.luv2code.com
Step 2 - Apply to Multiple Advices
@Aspect
@Component
public class MyDemoLoggingAspect {
@Pointcut("execution(* com.luv2code.aopdemo.dao.*.*(..))")
private void forDaoPackage() {}
@Before("forDaoPackage()")
public void beforeAddAccountAdvice() {
…
}
@Before("forDaoPackage()")
public void performApiAnalytics() {
…
}
www.luv2code.com
Benefits of Pointcut Declarations
www.luv2code.com
Aspect-Oriented Programming (AOP)
Combine Pointcuts
Problem
• How to apply multiple pointcut expressions to single advice?
• For example
www.luv2code.com
Combining Pointcut Expressions
• Combine pointcut expressions using logic operators
• AND (&&)
• OR (||)
• NOT (!)
www.luv2code.com
Combining Pointcut Expressions
• Works like an “if” statement
@Before(“expressionOne() || expressionTwo()”)
www.luv2code.com
Example:
• All methods in a package EXCEPT getter/setter methods
www.luv2code.com
Development Process Step-
By-S
tep
www.luv2code.com
Step 1- Create Pointcut Declaration
@Pointcut("execution(* com.luv2code.aopdemo.dao.*.*(..))")
private void forDaoPackage() {}
www.luv2code.com
Step 1- Create Pointcut Declaration
@Pointcut("execution(* com.luv2code.aopdemo.dao.*.*(..))")
private void forDaoPackage() {}
www.luv2code.com
Step 1- Create Pointcut Declaration
@Pointcut("execution(* com.luv2code.aopdemo.dao.*.*(..))")
private void forDaoPackage() {}
www.luv2code.com
Step 2 - Combine Pointcut Declarations
@Pointcut("execution(* com.luv2code.aopdemo.dao.*.*(..))")
private void forDaoPackage() {}
www.luv2code.com
Step 3 - Apply Pointcut Declaration to Advice(s)
…
@Before("forDaoPackageNoGetterSetter()")
public void beforeAddAccountAdvice() {
…
}
www.luv2code.com
Aspect-Oriented Programming (AOP)
Control Aspect Order
Problem
• How to control the order of advices being applied?
MyLoggingDemoAspect
beforeAddAccountAdvice
performApiAnalyticsAdvice
logToCloudAdvice
www.luv2code.com
Problem
• How to control the order of advices being applied?
MyLoggingDemoAspect
The order is
beforeAddAccountAdvice unde ned
fi
performApiAnalyticsAdvice
logToCloudAdvice
www.luv2code.com
To Control Order
• Refactor: Place advices in separate Aspects
www.luv2code.com
Development Process Step-
By-S
tep
www.luv2code.com
Step 1 - Refactor: Place advices in separate Aspects
Refactor
www.luv2code.com
Step 2 - Add @Order annotation
• Control order on Aspects using the @Order annotation
@Order(1)
public class MyCloudLogAspect {
…
}
www.luv2code.com
@Order
• We want the following order:
1. MyCloudLogAspect
2. MyLoggingDemoAspect
3. MyApiAnalyticsAspect
www.luv2code.com
@Order annotation
@Aspect
@Component
@Order(1)
public class MyCloudLogAspect {
…
} @Aspect
@Component
@Order(2)
public class MyLoggingDemoAspect {
…
}
@Aspect
@Component
@Order(3)
public class MyApiAnalyticsAspect {
…
}
www.luv2code.com
@Order annotation
• Lower numbers have higher precedence
www.luv2code.com
@Order annotation
• Example with negative numbers
www.luv2code.com
@Order annotation
• FAQ: What if aspects have the exact same @Order annotation?
@Order(1)
The order at
public class MyCloudLogAspect { … }
this point is
@Order(6) unde ned
fi
public class MyShowAspect { … }
@Order(6)
public class MyFunnyAspect { … } Will still
run AFTER
@Order(123)
MyCloudLogAspect
public class MyLoggingDemoAspect { … }
and BEFORE
MyLoggingDemoAspect
www.luv2code.com
Aspect-Oriented Programming (AOP)
Reading Method Arguments with JoinPoints
Problem
• When we are in an aspect (ie for logging)
www.luv2code.com
Development Process Step-
By-S
tep
www.luv2code.com
Step 1 - Access and display Method Signature
@Before("...")
public void beforeAddAccountAdvice(JoinPoint theJoinPoint) {
www.luv2code.com
Step 1 - Access and display Method Signature
@Before("...")
public void beforeAddAccountAdvice(JoinPoint theJoinPoint) {
www.luv2code.com
Step 2 - Access and display Method Arguments
@Before("...")
public void beforeAddAccountAdvice(JoinPoint theJoinPoint) {
// get args
Object[] args = theJoinPoint.getArgs();
www.luv2code.com
Aspect-Oriented Programming (AOP)
Progress Check
Progress Check
• @AfterReturning
• @Before advice type
• @AfterThrowing
• Pointcut Expressions
• @After
• JoinPoints
• @Around
www.luv2code.com
Aspect-Oriented Programming (AOP)
@AfterReturning Advice
Advice Types
www.luv2code.com
@AfterReturning Advice - Interaction
Logging Security
Aspect Aspect
TargetObject
@Before
MainApp AOP
Proxy public void doSomeStuff() {
// call target object …
targetObj.doSomeStuff(); @AfterReturning }
Logging Security
Aspect Aspect
www.luv2code.com
Advice - Interaction
@Before
@AfterReturning
Successful execution
(no exceptions)
www.luv2code.com
@AfterReturning Advice - Use Cases
• Most common
• Audit logging
• Post-processing Data
• Format the data or enrich the data (really cool but be careful)
www.luv2code.com
Example
• Create an advice that will run after a method call (success execution)
Target Object
AccountDAO
Logging
Aspect
List<Account> ndAccounts()
@AfterReturning
fi
www.luv2code.com
@AfterReturning Advice
• This advice will run after the method call (success execution)
fi
public void afterReturningFindAccountsAdvice() {
www.luv2code.com
Access the Return Value
• Need to access the return value of method called
Target Object
AccountDAO
Logging
Aspect
List<Account> ndAccounts()
@AfterReturning
fi
www.luv2code.com
Access the Return Value
@AfterReturning(
pointcut="execution(* com.luv2code.aopdemo.dao.AccountDAO. ndAccounts(..))",
fi
returning="result")
public void afterReturningFindAccountsAdvice() {
www.luv2code.com
Access the Return Value
@AfterReturning(
pointcut="execution(* com.luv2code.aopdemo.dao.AccountDAO. ndAccounts(..))",
fi
returning="result")
public void afterReturningFindAccountsAdvice(
JoinPoint theJoinPoint, List<Account> result) {
www.luv2code.com
Access the Return Value
@AfterReturning(
pointcut="execution(* com.luv2code.aopdemo.dao.AccountDAO. ndAccounts(..))",
fi
returning="result")
public void afterReturningFindAccountsAdvice(
JoinPoint theJoinPoint, List<Account> result) {
www.luv2code.com
Best Practices: Aspect and Advices
www.luv2code.com
Development Process - @AfterReturning
Step-
By-S
tep
1. Prep Work: Add constructors to Account class
fi
3. Update main app to call the new method: ndAccounts()
fi
4. Add @AfterReturning advice
www.luv2code.com
Aspect-Oriented Programming (AOP)
@AfterReturning Advice - Modify Return Value
@AfterReturning Advice - Use Cases
• Most common
• Audit logging
• Post-processing Data
• Format the data or enrich the data (really cool but be careful)
www.luv2code.com
Post-Process / Modify Data
Target Object
AccountDAO
Logging
Aspect
List<Account> ndAccounts()
@AfterReturning
fi
Post-process the data
(we can modify it)
www.luv2code.com
Modify the Return Value
@AfterReturning(
pointcut="execution(* com.luv2code.aopdemo.dao.AccountDAO. ndAccounts(..))",
fi
returning="result")
public void afterReturningFindAccountsAdvice(
JoinPoint theJoinPoint, List<Account> result) {
www.luv2code.com
Modify the Return Value
@AfterReturning(
pointcut="execution(* com.luv2code.aopdemo.dao.AccountDAO. ndAccounts(..))",
fi
returning="result")
public void afterReturningFindAccountsAdvice(
JoinPoint theJoinPoint, List<Account> result) {
tempAccount.setName("Daffy Duck");
}
}
www.luv2code.com
Post-Process / Modify Data
Target Object
AccountDAO
Logging
Aspect
List<Account> ndAccounts()
@AfterReturning
fi
Calling program will get the
modified result
www.luv2code.com
Calling program
fi
List<Account> theAccounts = theAccountDAO. ndAccounts();
fi
Logging
Aspect
@AfterReturning
www.luv2code.com
Aspect-Oriented Programming (AOP)
@AfterThrowing Advice
Advice Types
www.luv2code.com
@AfterThrowing Advice - Interaction
Logging Security
Aspect Aspect
TargetObject
@Before
MainApp AOP
Proxy public void doSomeStuff() {
// call target object …
targetObj.doSomeStuff(); @AfterThrowing } Exception
Logging Security
Aspect Aspect
www.luv2code.com
Advice - Interaction
@Before
Exception
@AfterThrowing
www.luv2code.com
Sequence Diagram
www.luv2code.com
@AfterThrowing Advice - Use Cases
www.luv2code.com
Example
• Create an advice that will run after an exception is thrown
Target Object
AccountDAO
Logging
Aspect
List<Account> ndAccounts()
@AfterThrowing
fi
www.luv2code.com
@AfterThrowing Advice
• This advice will run after an exception is thrown
fi
public void afterThrowingFindAccountsAdvice() {
www.luv2code.com
Access the Exception object
• Need to access the exception object
Target Object
AccountDAO
Logging
Aspect
List<Account> ndAccounts()
@AfterThrowing
fi
www.luv2code.com
Access the Exception
@AfterThrowing(
pointcut="execution(* com.luv2code.aopdemo.dao.AccountDAO. ndAccounts(..))",
fi
throwing="theExc")
public void afterThrowingFindAccountsAdvice() {
www.luv2code.com
Access the Exception
@AfterThrowing(
pointcut="execution(* com.luv2code.aopdemo.dao.AccountDAO. ndAccounts(..))",
fi
throwing="theExc")
public void afterThrowingFindAccountsAdvice(
JoinPoint theJoinPoint, Throwable theExc) {
www.luv2code.com
Access the Exception
@AfterThrowing(
pointcut="execution(* com.luv2code.aopdemo.dao.AccountDAO. ndAccounts(..))",
fi
throwing="theExc")
public void afterThrowingFindAccountsAdvice(
JoinPoint theJoinPoint, Throwable theExc) {
www.luv2code.com
Exception Propagation
• At this point, we are only intercepting the exception (reading it)
www.luv2code.com
Exception Propagation
www.luv2code.com
Development Process - @AfterThrowing
Step-
By-S
tep
1. In Main App, add a try/catch block for exception handling
www.luv2code.com
Aspect-Oriented Programming (AOP)
@After Advice
Advice Types
www.luv2code.com
@After (finally) Advice - Interaction
Logging Security
Aspect Aspect
TargetObject
@Before
MainApp AOP
Proxy public void doSomeStuff() {
// call target object …
targetObj.doSomeStuff(); @After ( nally) }
fi
Logging Security
Aspect Aspect
www.luv2code.com
Advice - Interaction
@Before
@After
fi
completed. Regardless of
outcome / exceptions
www.luv2code.com
Sequence Diagram
www.luv2code.com
@After Advice - Use Cases
www.luv2code.com
Example
• Create an advice to run after the method ( nally … success/failure)
fi
Target Object
AccountDAO
Logging
Aspect
List<Account> ndAccounts()
@After
fi
www.luv2code.com
@After Advice
• This advice will run after the method ( nally … success/failure)
fi
@After("execution(* com.luv2code.aopdemo.dao.AccountDAO. ndAccounts(..))")
fi
public void afterFinallyFindAccountsAdvice() {
fi
}
www.luv2code.com
@After Advice - Tips
• The @After advice should be able to run in the case of success or error
www.luv2code.com
Development Process - @After
Step-
By-S
tep
1. Prep work
www.luv2code.com
Aspect-Oriented Programming (AOP)
@Around Advice
Advice Types
www.luv2code.com
@Around Advice - Interaction
@Around
Logging TargetObject
MainApp AOP Aspect
www.luv2code.com
Advice - Interaction
@Around
Logging
Aspect
www.luv2code.com
@Around Advice - Use Cases
fi
• How long does it take for a section of code to run?
• Managing exceptions
www.luv2code.com
Our FortuneService Example - Revisited
Target Object
FortuneService
String getFortune()
www.luv2code.com
Sequence Diagram
www.luv2code.com
ProceedingJoinPoint
• Your code can use the proceeding join point to execute target method
www.luv2code.com
Example
• Create an advice for instrumentation / pro ling code
fi
• How long does it take for a section of code to run?
@Around
Logging
Aspect Target Object
FortuneService
String getFortune()
www.luv2code.com
@Around Advice
@Around("execution(* com.luv2code.aopdemo.service.*.getFortune(..))")
public Object afterGetFortune(
ProceedingJoinPoint theProceedingJoinPoint) throws Throwable {
www.luv2code.com
@Around Advice
@Around("execution(* com.luv2code.aopdemo.service.*.getFortune(..))")
public Object afterGetFortune(
ProceedingJoinPoint theProceedingJoinPoint) throws Throwable {
return result;
}
www.luv2code.com
Development Process - @Around
Step-
By-S
tep
1. Create Traf cFortuneService
fi
2. Update main app to call Traf cFortuneService
fi
3. Add @Around advice
www.luv2code.com
Aspect-Oriented Programming (AOP
• This gives you ne-grained control over how the target method is called
fi
www.luv2code.com
Sequence Diagram
www.luv2code.com
Handle Exception
@Around("execution(* com.luv2code.aopdemo.service.*.getFortune(..))"
www.luv2code.com
Handle Exception
@Around("execution(* com.luv2code.aopdemo.service.*.getFortune(..))"
www.luv2code.com
Handle Exception
@Around("execution(* com.luv2code.aopdemo.service.*.getFortune(..))"
try
{
www.luv2code.com
Handle Exception
@Around("execution(* com.luv2code.aopdemo.service.*.getFortune(..))"
try
{
// log exception
System.out.println("@Around advice: We have a problem " + exc)
// handle and give default fortune ... use this approach with caution
result = "Nothing exciting here. Move along!"
www.luv2code.com
Handle Exception
@Around("execution(* com.luv2code.aopdemo.service.*.getFortune(..))"
try
{
// log exception
System.out.println("@Around advice: We have a problem " + exc)
// handle and give default fortune ... use this approach with caution
result = "Nothing exciting here. Move along!"
return result
;
}
www.luv2code.com
Development Process - @Around
Step-
By-S
tep
1. Add trip wire to simulate an exceptio
www.luv2code.com
Aspect-Oriented Programming (AOP
www.luv2code.com
Sequence Diagram
www.luv2code.com
Rethrow Exception
@Around("execution(* com.luv2code.aopdemo.service.*.getFortune(..))"
www.luv2code.com
Rethrow Exception
@Around("execution(* com.luv2code.aopdemo.service.*.getFortune(..))"
try
{
return result;
}
www.luv2code.com
Rethrow Exception
@Around("execution(* com.luv2code.aopdemo.service.*.getFortune(..))"
try
{
return result;
}
// log exception
System.out.println("@Around advice: We have a problem " + exc)
// rethrow it
throw exc
;
www.luv2code.com
FAQ: JoinPoint vs ProceedingJoinPoint
• @Around
www.luv2code.com
Aspect-Oriented Programming (AOP)
AOP and Spring MVC
Goal
• Add AOP Logging support to our Spring MVC CRUD app
www.luv2code.com
Big Picture
1 2 3 4
Employee Employee Employee
Controller Service DAO
Web 5
Browser
Thymeleaf page
www.luv2code.com
Logging Aspect
www.luv2code.com
Development Process Step-
By-S
tep
fi
2. Create Aspect
www.luv2code.com
Step 1: Spring Boot AOP Starter
• Add the dependency for Spring Boot AOP Starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
www.luv2code.com
Development Process Step-
By-S
tep
fi
2. Create Aspect
www.luv2code.com
Pointcut Declarations
Only match on
these three
packages
www.luv2code.com