Post

Udemy Spring boot course: Section 10 AOP

Git Repo for this sesion

  • put here

Resources

Brief intro example

  • we have the following architecture
    • alt-text
  • we have the following code for our dao
    1
    2
    3
    
    public void addAccount(Account account, String userId) {
      entityManager.persist(account);
    }
    
  • lets say manager asks us to implement logging with the following requirements
    • modular - can be used for other methods
    • happens before and/or after the method we call -lets say manager asks us to do the same thing but to implement security

We could do the following but we would have to reuse the code in other places, just copying and pasting (NOT GOOD PRACTICE)

1
2
3
4
5
public void addAccount(Account account, String userId) {
  // logging +4 lines
  // security +5 lines
  entityManager.persist(account);
}

This introduces Two big Problems

  • Code Tangling
    • logging and security code tangled together along with business logic
  • Code Scattering
    • If we need to update the logging or security codes, we have to do so in numerous places

Other Quick Possible solutions that could work

  • Inheritence
    • create a abstract class of Logging & Security and just have the classes inherit from it
    • This could work but but every class would need to inherit from these classes
    • Plus java doesn’t support multile inheritence so it could inherit from Logging or Security but not both
  • Delegation
    • classes delegate logging and security calls
    • we would still need to uppdate all those classes that want to implement logging/security and put the call to those methods in there

Next section below talks about the BEST SOLUTION (AOP)

Aspect oriented programming

  • Programming paradigm based on this concept of a Aspect
  • AOP is an extension of OOP
  • Aspect
    • encapsulates cross-cutting concerns
    • Where cross-cutting concerns are just logic like security, logic that is the same for multiple methods
  • alt-text

AOP Terminology

TermDescription
Aspecta standard code/feature that is scattered across multiple places in the application and is typically different than the actual Business Logic (for example, Transaction management). Each aspect focuses on a specific cross-cutting functionality
Advicethe action taken by the aspect in a specific joinpoint
Joinpointit’s a particular point during execution of programs like method execution, constructor call, or field assignment
Pointcuta regular expression that matches a joinpoint. Each time any join point matches a pointcut, a specified advice associated with that pointcut is executed
Weavingthe process of linking aspects with targeted objects to create an advised object

Advice Types

AdviceDescription
BeforeRun advice before the method execution.
AfterRun advice after the method execution, regardless of its outcome.
After-returningRun advice after the method execution, only if the method completes successfully.
After-throwingRun advice after the method execution, only if the method exits by throwing an exception.
AroundRun advice before and after the advised method is invoked.

Point Cut expressions

ExpressionDescription
BeforeRun advice before the method execution.

Pros / Cons

ProsCons
Reusable modulesToo many Aspects
Resolve code tanglingApp flow can be hard to follow
Resolve code scatterMinor performance cost for aspect execution (runtime weaving)
Apply selectively based on configuration 

Weaving

  • Two leading AOP frameworks for Java
    • Spring AOP
    • AspectJ

Spring AOP

  • support out of the box
  • run-time weaving utilizing proxy-based

Proxy-based

  • alt-text

Runtime weaving

  • Runtime weaving, in the context of Aspect-Oriented Programming (AOP), refers to the process of applying aspects (cross-cutting concerns) to a running program or application.

AspectJ

  • Original AOP framework
  • provides complete support for AOP
  • released in 2001

Runtime weaving vs Compile-time weaving

  • Runtime weaving involves integrating aspect code with the running application at runtime, typically during the execution of the program.
  • This is in contrast to compile-time weaving, where aspect code is integrated with the application during the compilation phase.
  • Runtime weaving allows you to add, modify, or remove aspects without recompiling the entire application, making it more flexible and adaptable.

Spring AOP pros/cons

ProsCons
Simplier to use than AspectJonly supports method level join points
Uses Proxy patternCan only apply aspects to beans created by Spring app context
Can migrate to AspectJ when using @Aspect annotationMinor performance costs for using run-time weaving

AspectJ pros/cons

ProsCons
Support all Join pointsCompile-time weaving required extra compilation step
works with any POJO, not just beans from app contextAspectJ pointcut syntax can become complex
Faster performance compared to Spring AOP 
Complete AOP support 

Parameter Pattern Wildcards

SymbolDescription
()matches a method with No arguments
(*)matches a method 1 argument of any type
(..)matches a method with 0 or more arguments of any type
(int,..)matches a method with 1 argument of type int, or multiple arguments with the first being an int
(com.tpool.Account)1 argument of type Account object with full class path

Should you use Spring AOP or AspectJ

  • Spring AOP is a lightweight version of AspectJ
  • it solves common enterprise problems
  • start off with Spring AOP and if you have complex business needs go ahead and use AspectJ

Pointcut Expressions

  • Spring AOP uses AspectJ pointcut expressions
  • Spring AOP has no support for static methods
  • Pointcut
    • It is a predicate expression that determines the Join points, hence allowing us to control the advice execution
    • In simple words, a pointcut expression is like a Regular Expression which determines the classes and methods where the Advice will be executed
  • Declaring a pointcut is simple, all you need is annotate the method with @Pointcut(POINTCUT_EXPRESSION)
  • alt-text
    • matches on specific class method name
  • alt-text
    • just matches on any class that has the method name
  • alt-text
    • matches on any method name starting with add like addStudent(), addLog(), addAnything()
  • alt-text
    • matches on any return type
    • matches on any method that starts with processCreditCard*
  • @Before("execution(* com.tpool.aopdemo.dao.*.*(..))")
    • this matches for all methods in a given package
    • com.tpool.aopdemo.dao - tells spring this is the base package
    • .* - tells spring any class
    • .* - tells spring any method name
    • (..) - tels spring any number of arguments of any type

Pointcut Declarations

  • way of reusing our pointcut expressions to multiple advices
  • Declaring pointcut-declarations
    • alt-text
  • Applying pointcut declaration to advice
    • alt-text

Combining Pointcut Declarations

  • we can apply multiple pointcut declarations using logic operators
  • The example we want to do is apply pointcut declarations to “All methods in a package EXCEPT getter/setter methods”
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// just a collection of related advices
@Aspect
@Component
public class MyDemoLoggingAspect {

    // all methods that are in the dao package
    @Pointcut("execution(* com.tpool.aopdemo.dao.*.*(..))")
    private void forDaoPackage(){};

    @Pointcut("execution(* com.tpool.aopdemo.dao.*.get*(..))")
    private void DaoGetters(){};

    @Pointcut("execution(* com.tpool.aopdemo.dao.*.set*(..))")
    private void DaoSetters(){};

    // all in the dao package but no getters or setters
    @Pointcut("forDaoPackage() && !(DaoGetters() || DaoSetters())")
    private void forDaoPackageNoGetterSetter(){}

    @Before("forDaoPackageNoGetterSetter()")
    public void logging(){
        System.out.println("logging here ...");
    }
}

Controlling Aspect order with @Order

  • We want the advices to run in a certain order
  • Lower numbers have higher precedence
  • negative numbers are allowed
  • numbers do not have to be consecutive
    • ex. 1, 5, 10, 12
  • alt-text
    • we have to break up the single aspect into multiple to get that fine grain control of the order
  • alt-text
    • the order in which the aspects will run in
  • alt-text
  • What if two aspects have the same order number?
    • it is considered undefined, so expect wacky results
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Aspect
public class AopExpressions {
    // all methods that are in the dao package
    @Pointcut("execution(* com.tpool.aopdemo.dao.*.*(..))")
    public void forDaoPackage(){};

    // getter methods
    @Pointcut("execution(* com.tpool.aopdemo.dao.*.get*(..))")
    public void DaoGetters(){};

    // setter methods
    @Pointcut("execution(* com.tpool.aopdemo.dao.*.set*(..))")
    public void DaoSetters(){};

    // all in the dao package but no getters or setters
    @Pointcut("forDaoPackage() && !(DaoGetters() || DaoSetters())")
    public void forDaoPackageNoGetterSetter(){}
}
1
2
3
4
5
6
7
8
9
@Aspect
@Component
@Order(1)
public class LoggingAspect {
    @Before("com.tpool.aopdemo.aspect.AopExpressions.forDaoPackageNoGetterSetter()")
    public void CloudLog(){
        System.out.println("=======> Cloud log");
    }
}
1
2
3
4
5
6
7
8
9
@Aspect
@Component
@Order(2)
public class ApiAspect {
    @Before("com.tpool.aopdemo.aspect.AopExpressions.forDaoPackageNoGetterSetter()")
    public void apiLog(){
        System.out.println("=======> API log");
    }
}
1
2
3
4
5
6
7
8
9
@Aspect
@Component
@Order(3)
public class SecurityAspect {
    @Before("com.tpool.aopdemo.aspect.AopExpressions.forDaoPackageNoGetterSetter()")
    public void security(){
        System.out.println("=======> security check");
    }
}

How to access method signature and method parameters in the advice

  • we want to use the method signature and parameters in the advice for things like logging the method that was called
  • We want to access the return value for a method
  • alt-text
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Aspect
@Component
@Order(3)
public class SecurityAspect {
    @Before("com.tpool.aopdemo.aspect.AopExpressions.forDaoPackageNoGetterSetter()")
    public void security(JoinPoint joinPoint){
        System.out.println("=======> security check");

        // method signature
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        System.out.println("Method: " + methodSignature);


        // go through all of the arguments
        System.out.println("args");
        Object[] args = joinPoint.getArgs();
        for(var arg: args) {
            System.out.println(arg);
        }
        System.out.println("=== end of args ===");

    }
}

Accessing the return value for a @AfterReturning advice

  • We want to access the return value for a method
  • alt-text
1
2
3
4
5
6
7
8
9
@AfterReturning(
        pointcut = "execution(* RetrieveMemberships(..))",
        returning = "result"
)
public void afterReturningResult(List<String> result) {
    // remove all of the elements in the list
    result.clear();
    result.addAll(Arrays.asList("You", "are", "awesome", "!"));
}

@AfterThrowing

  • advice is executed after an exception is thrown from the method being called
  • alt-text
  • What its great for
    • log exceptions
    • perform auding on the exception
    • notify devops team via email or sms

How to access exception object

  • use the throwing="theException"
  • the throwing=theException has to match what is in the parameters for Throwable theException
1
2
3
4
5
6
7
8
9
10
@AfterThrowing(
        pointcut = "execution(* com.tpool.aopdemo.dao.MembershipDAOImpl.simulateException(..) )",
        throwing = "throwingException"
)
public void afterThrowing(Throwable throwingException){
    System.out.println("you threw an exception buddy");
    
    // print out the exception message
    System.out.println(throwingException.getMessage());
}

@Around

  • Runs before and after method execution
  • Most common use cases
    • logging
    • auditing
    • security
    • pre-processing & post-processing
  • Ability to manage exeptions
    • swallow, handle, stop exceptions
  • When using @Around advice you get a reference to the ProceedingJoinPoint
    • this is a handle to the target method
    • your code can use this to execute the target method

Example below

  • ProceedingJointPoint
  • notice proceedingJointPoint.proceed() gets the method to be executed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Around("execution(* com.tpool.aopdemo.dao.MembershipDAOImpl.getAccount(..) )")
public Object aroundDemo(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
    // start time
    long begin = System.currentTimeMillis();

    // calls getAccount()
    Object result = proceedingJoinPoint.proceed();

    // end time
    long end = System.currentTimeMillis();

    long duration = end - begin;
    System.out.println("\n======> DURATION " + duration + " milliseconds");

    return result;
}

Exception Handling

  • for an exception thrown from proceeding join point we can
    • you can handle / swallow / stop the exception
    • or you can simply re-throw the exception
  • alt-text
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Around("execution(* com.tpool.aopdemo.dao.MembershipDAOImpl.getAccount(..) )")
public Object handleException(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    System.out.println("beginning of handleException()");

    Object result;
    try {
        result = proceedingJoinPoint.proceed();
    }
    catch (Exception e) {
        result = "exception occured, but we don't really care";
    }

    System.out.println("end of handleException()");
    return result;
}

If we wanted to rethrow the exception we’d simply use throw again in the catch block like this

catch (Exception e) {
    throw e;
}

Enabling Spring AOP

  • Add to the pom.xml
    • it will be automatically enabled after that
    • in the old days you would have to use the @EnableAspectJAutoProxy annotation

Example using @Before

Development process

  • Create target object: AccountDao
  • Create main app
  • Create an Aspect with @Before advice

Create target object: AccountDao

1
2
public interface AccountDAO {
  void add Account();
1
2
3
4
5
@Component
public class AccountDAOImpl implements AccountDAO {
  void add Account() {
    System.out.println("ADDING ACCOUNT");
  }

Create main app

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@SpringBootApplication
public class AppdemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(AppdemoApplication.class, args);
	}

	@Bean
	public CommandLineRunner commandLineRunner(AccountDAO accountDAO) {
		return runner -> {
			demoTheBeforeAdvice(accountDAO);
		};
	}

	private void demoTheBeforeAdvice(AccountDAO accountDAO) {
		accountDAO.addAccount();
	}

}

Create an Aspect with @Before advice

1
2
3
4
5
6
7
8
9
10
11
@Aspect
@Component
public class MyDemoLoggingAspect {
    // pointcut expression goes between the parens of @Before
    @Before("execution(public void addAccount())")
    public void loggingAdvice() {
        System.out.println("===============================");
        System.out.println("===> Logging stuff here... <===");
        System.out.println("===============================");
    }
}

When we run our code we get the following

1
2
3
4
===============================
===> Logging stuff here... <===
===============================
class com.tpool.aopdemo.dao.AccountDAOImpl: DOING MY DB: WORK ADDING AN ACCOUNT
This post is licensed under CC BY 4.0 by the author.