AOP, namely aspect oriented programming, its core idea is to divide business into core business and non-core business. For example, a forum system, user login, Posting and so on are core functions, while log statistics and so on are non-core functions.

In Spring Boot AOP, non-core business functions are defined as facets. After both core and non-core functions are developed, the two are woven together, which is AOP.

The purpose of AOP is to separate the logic that is irrelevant to the business but needs to be used by the business, so as to reduce duplicate code, reduce coupling between modules, and facilitate future system expansion and maintenance.

Today, I’ll implement AOP on a simple program that prints User information, where the back end takes the User object in a POST request and prints it logic.

First put the code for the user to print the service logic:

Service layer:

package com.example.springbootaop.service.impl; import com.example.springbootaop.model.User; import com.example.springbootaop.service.UserService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component public class UserServiceImpl implements UserService {/** * implements UserService */ private Logger Logger = LoggerFactory.getLogger(this.getClass()); @override public void printUserInfo(User User) {logger.info(" User id: "+ user.getid ()); Logger.info (" username: "+ user.getUsername()); Logger.info (" user name: "+ user.getnickName ()); }}Copy the code

The Control layer:

package com.example.springbootaop.api; import com.example.springbootaop.model.User; import com.example.springbootaop.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController public class UserAPI { @Autowired private UserService userService; @PostMapping("/user") public String printUser(@RequestBody User user) { userService.printUserInfo(user); Return "Print finished!" ; }}Copy the code

User is a simple wrapper class that is not shown here, and the entire sample program code address is given at the end of the article.

1. What exactly is different about AOP

Here we just implement a simple logic, print the user information, this is our core function today.

If we were to add non-core functionality at this point: execute a method before printing and a method after printing. If you don’t know AOP, you might change the Control layer’s methods to the following form:

@postmapping ("/user") public String printUser(@requestBody user user) {PostMapping("/user") public String printUser(@requestBody user user) { / / execution core business userService printUserInfo (user); // doAfter(); . Return "Print finished!" ; }Copy the code

If there are more methods and more businesses, all methods of all controllers will have to be changed once the logic of non-core businesses changes, which is very troublesome and has redundant codes and high coupling degree.

At this point, AOP is needed to solve the problem.

AOP simply requires us to define a separate facet, write the non-core business logic in it, and weave it into the core functionality without changing the Service or Control layer.

Programming terminology and common annotations in AOP

Before we dive into AOP, we need to look at some common terms:

  • Facets: Non-core business functions are defined as facets. For example, the logging function of a system, which runs through the logic of the entire core business, is called the aspect
  • Pointcuts: Cut into which methods of which classes
  • Notification: What to do before/after method execution or before and after execution
    • Pre-notification: executed before the proxied method
    • Postnotification: executed after the proxied method
    • Return notification: executed after the proxied method returns normally
    • Exception notification: executed when an exception is thrown by a proxy method
    • Surround advice: a powerful and flexible advice in AOP that integrates pre – and post-advice
  • Aspect: What to do when and where (pointcut + notification)
  • Weaving: Adding aspects to objects is the process of generating proxy objects and putting aspects into the process (in short, the process of adding aspect logic to core business logic)

In Spring Boot, to develop AOP using the @AspectJ annotation, we first need to introduce the following dependencies in POM.xml:

<! -- Spring Boot AOP --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>Copy the code

Then you’re ready for AOP development!

Here are some common notes that you can use in conjunction with the following examples:

  • @PointcutDefine the point of tangency
  • @BeforePre notice
  • @AfterThe rear notice
  • @AfterReturningReturn to inform
  • @AfterThrowingAbnormal notice
  • @AroundSurrounding the notification

3. Define the section

Create a new aop package and create a new class as our aop class. First, release the aop class code:

package com.example.springbootaop.aop; import org.aspectj.lang.annotation.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @aspect @Component public class LogAspect {/** * log printing */ private Logger Logger = LoggerFactory.getLogger(this.getClass()); /** * Use Pointcut to define pointcuts for this method, that is, all methods in UserService are pointcuts. <br> * Here you define the pointcut above the log method, and then you can set the position of each notification simply by filling in the pointcut method "log()" in the annotations below Before, After, etc. </li> * <li>execution: On behalf of any return value method < / li > * < li > com. Example. Springbootaop. Service. Impl. UserServiceImpl: the fully qualified class name < / li > * < li > (..) That said any parameter < / li > * < / ul > * / the @pointcut (" execution (* com. Example. Springbootaop. Service. Impl. UserServiceImpl. * (..) )") public void log() {} /** * Before("log()") public void doBefore() {logger.warn(" Before calling method: "); Logger.warn (" Request received!" ); } /** * After("log()") public void doAfter() {logger.warn(" After calling: "); Logger. warn(" Print request content completed!" ); } @afterreturning ("log()") public void doReturning() {logger.warn(" AfterReturning: "); Logger.warn (" Finish returning content!" ); */ @afterthrowing ("log()") public void doThrowing() {logger.error(" method throws an exception! ") ); }}Copy the code

The Aspect class needs to be annotated @Aspect to indicate that it is a Aspect class, and don’t forget to annotate @Component.

Let’s do it step by step.

The first step is to define a Pointcut. Simply define an empty method and use the @pointcut annotation on it.

  • executionTriggered when the method is executed
  • *A method that represents any return value
  • com.example.springbootaop.service.impl.UserServiceImplThe fully qualified name of the class to be woven into
  • (..)Represents an arbitrary parameter

Once the pointcuts are defined, we can define the method logic for each notification, which is our aspect logic, that is, non-core business logic.

Above the doBefore method, we use the @before annotation to indicate that the doBefore method is pre-notification logic and will be executed Before it is woven into the method. We define the log method as the pointcut, and then fill in the pointcut method name in each of the following notification annotations.

We don’t need to define all the notifications, just the ones we need.

In fact, if the Pointcut methods log and @pointcut are not defined, you can still write execution expressions directly in the annotations of individual notifications, for example:

Prior notification: / * * * * Before being proxy method call / @ Before (" execution (* com. Example. Springbootaop. Service. Impl. UserServiceImpl. * (..) )") public void doBefore(JoinPoint JoinPoint) {logger.warn(" before calling method: "); Logger.warn (" Request received!" ); }Copy the code

But in most cases it’s not recommended, it’s complicated.

Let’s test this by sending a request:

From this, we can also find the order of execution of each notification:

Before -> AfterReturning -> After
Copy the code

4. Surround notification

Wrap advice is the most powerful form of advice in AOP and can implement both pre – and post-advice, although it is less controllable and generally not needed without significant changes to the business logic. We add the following circular notification method to the above section:

/ / @around ("log()") public void Around(ProceedingJoinPoint joinPoint) {logger.warn(" Before running: "); try { joinPoint.proceed(); } catch (Throwable e) { e.printStackTrace(); } logger.warn(" after execution of wrap notification "); }Copy the code

The notification method has a ProceedingJoinPoint type parameter that calls the original method through its PROCEED method. Note that the wrap notification overrides the original method logic if the code above does not call joinPoint.proceed(); This statement does not execute the original woven method. So the wrap notification must call the arguments’ proceed method, which is implemented by reflection on the woven method call.

Test again as follows:

5, notification method parameter transfer

Each of the above notification methods has no parameters. In fact, the notification method can accept parameters woven into the method. The method argument we weave in above is a User object, so the notification method can also accept this argument. We changed the pre-notification method as follows:

*/ @before ("log() &&args (user)") public void doBefore(user user) {logger.warn(" Before calling method: "); Logger.warn (" Request received!" ); Logger.warn (" Get user ID: "+ user.getid ()); }Copy the code

Test results:

Add an args option to the end of the comment and write the parameter name.

Note that the parameters of the notification method must correspond to the parameters woven into the method. For example:

Public void print(User User, int num) {... } @before ("log() &&args (user, num)") public void doBefore(user user, int num) {... }Copy the code

6,

AOP is actually very convenient to use, greatly reducing the coupling between related functions and keeping the whole system in order.

Defining the aspect, then defining the pointcut, and then implementing the aspect logic (the individual notification methods) completes a simple aspect.

Sample repository address