Spring AOP notifications are divided into five categories:
Before advice: Execute in front of the connection point. The preview will not affect the execution of the connection point unless an exception is thrown here.
Normal return notification [After returning advice]: Execute after normal execution of the connection point is completed. If an exception is thrown by the connection point, it will not be executed.
Exception return notification [After throwing advice]: Execute after an exception is thrown by the connection point.
Return notification [After (finally) advice]: After execution is completed, the contents of the return notification will be executed whether it is completed normally or an exception is thrown.
Around advice: Around advice surrounds the connection point, such as before and after a method call. This is the most powerful notification type, which can customize some operations before and after method calls.
The surrounding notification also needs to decide whether to continue processing the join point (calling the process method of the ProceedingJoinPoint) or interrupt execution.
Next, we will test five notification types by writing a sample program:
Define interface
package com.chenqa.springaop.example.service;public interface BankService { /** * Simulated bank transfer* @param from account* @param to account* @param account Transfer amount* @return */ public boolean transfer(String form, String to, double account);}Write implementation classes
package com.chenqa.springaop.example.service.impl;import com.chenqa.springaop.example.service.BankService;public class BCMBankServiceImpl implements BankService { public boolean transfer(String form, String to, double account) { if(account<100) { throw new IllegalArgumentException("Minimum transfer amount cannot be less than 100 yuan"); } System.out.println(form+"transfer to"+to+"Bank Account"+account+"yuan"); return false; }}Modify the spring configuration file and add the following:
<!-- bankService bean --> <bean id="bankService"/> <!-- Section--> <bean id="myAspect"/> <!-- aop configuration--> <aop:config> <aop:aspect ref="myAspect"> <aop:pointcut expression="execution(* com.chenqa.springaop.example.service.impl.*.*(..))" id="pointcut"/> <aop:before method="before" pointcut-ref="pointcut"/> <aop:after method="after" pointcut-ref="pointcut"/> <aop:after-returning method="afterReturning" pointcut-ref="pointcut"/> <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut"/> <aop:around method="around" pointcut-ref="pointcut"/> </aop:aspect> </aop:config>
Writing a test program
ApplicationContext context = new ClassPathXmlApplicationContext("spring-aop.xml"); BankService bankService = context.getBean("bankService", BankService.class); bankService.transfer("Zhang San", "Li Si", 200); Output after execution:
Change 200 in the test program to 50, and then output after execution:
From the test results, it can be seen that the execution order of the five notifications is:
Pre-notification → Surround notification → Normal return notification/Exception return notification → Return notification, you can perform multiple times to view.
Case 1: A method is only intercepted by one Aspect class
When a method is intercepted by only one Aspect, in what order are the different advices in this Aspect executed? Please see:
Add PointCut class
This pointcut is used to intercept all methods in all classes under the test package.
package test;import org.aspectj.lang.annotation.Pointcut;public class PointCuts { @Pointcut(value = "within(test.*)") public void aopDemo() { }}Add Aspect class
The advice in this class will use the pointcut above. Please refer to the value attribute of each advice when using it.
package test;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.*;import org.springframework.stereotype.Component;@Component@Aspectpublic class Aspect1 { @Before(value = "test.PointCuts.aopDemo()") public void before(JoinPoint joinPoint) { System.out.println("[Aspect1] before advice"); } @Around(value = "test.PointCuts.aopDemo()") public void around(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("[Aspect1] around advice 1"); pjp.proceed(); System.out.println("[Aspect1] around advice2"); } @AfterReturning(value = "test.PointCuts.aopDemo()") public void afterReturning(JoinPoint joinPoint) { System.out.println("[Aspect1] afterReturning advice"); } @AfterThrowing(value = "test.PointCuts.aopDemo()") public void afterThrowing(JoinPoint joinPoint) { System.out.println("[Aspect1] afterThrowing advice"); } @After(value = "test.PointCuts.aopDemo()") public void after(JoinPoint joinPoint) { System.out.println("[Aspect1] afterThrowing advice"); } @After(value = "test.PointCuts.aopDemo()") public void after(JoinPoint joinPoint) { System.out.println("[Aspect1] after advise"); }}Add a test controller
Add a controller for testing. There is only one method in this controller, but it will handle differently according to the parameter values: one is to return an object normally, and the other is to throw an exception (because we want to test the @AfterThrowing advice)
package test;import test.exception.TestException;import org.springframework.http.HttpStatus;import org.springframework.web.bind.annotation.*;@RestController@RequestMapping(value = "/aop")public class AopTestController { @ResponseStatus(HttpStatus.OK) @RequestMapping(value = "/test", method = RequestMethod.GET) public Result test(@RequestParam boolean throwException) { // case 1 if (throwException) { System.out.println("throw an exception"); throw new TestException("mock a server exception"); } // case 2 System.out.println("test OK"); return new Result() {{ this.setId(111); this.setName("mock a Result"); }}; } public static class Result { private int id; private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }}Test normal situation
Enter the following URL directly in the browser and enter: http://192.168.142.8:7070/aoptest/v1/aop/test?throwException=false 1
We will see the output result:
[Aspect1] around advice 1[Aspect1] before advice OK[Aspect1] around advice2[Aspect1] after advice[Aspect1] afterReturning advice
Test exceptions
Directly enter the following URL in the browser and press Enter: http://192.168.142.8:7070/aoptest/v1/aop/test?throwException=true 1
We will see the output result:
[Aspect1] around advice 1[Aspect1] before advicethrow an exception[Aspect1] after advice[Aspect1] afterThrowing advice
in conclusion
When a method is intercepted by only one aspect class, the advice inside the aspect class will be executed in the following order:
Normal situation:
Exception:
Case 2: The same method is intercepted by multiple Aspect classes
Here is an example that is intercepted by two aspect classes.
In some cases, for two different aspect classes, regardless of whether their advice uses the same pointcut or different pointcuts, it may cause the same method to be intercepted by multiple aspect classes. So, in this case, in what order do the advice in these multiple Aspect classes execute? Please see:
The pointcut class remains unchanged
Add a new aspect class
package test;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.*;import org.springframework.stereotype.Component;@Component@Aspectpublic class Aspect2 { @Before(value = "test.PointCuts.aopDemo()") public void before(JoinPoint joinPoint) { System.out.println("[Aspect2] before advice"); } @Around(value = "test.PointCuts.aopDemo()") public void around(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("[Aspect2] around advice 1"); pjp.proceed(); System.out.println("[Aspect2] around advice2"); } @AfterReturning(value = "test.PointCuts.aopDemo()") public void afterReturning(JoinPoint joinPoint) { System.out.println("[Aspect2] afterReturning advice"); } @AfterThrowing(value = "test.PointCuts.aopDemo()") public void afterThrowing(JoinPoint joinPoint) { System.out.println("[Aspect2] afterThrowing advice"); } @After(value = "test.PointCuts.aopDemo()") public void after(JoinPoint joinPoint) { System.out.println("[Aspect2] afterThrowing advice"); } @After(value = "test.PointCuts.aopDemo()") public void after(JoinPoint joinPoint) { System.out.println("[Aspect2] after advise"); }}The test controller is also unchanged
Still use the Controller above. But now both aspect1 and aspect2 intercept the methods in the controller.
Continue to test below!
Test normal situation
Enter the following URL directly in the browser and enter: http://192.168.142.8:7070/aoptest/v1/aop/test?throwException=false 1
We will see the output result:
[Aspect2] around advice 1[Aspect2] before advice[Aspect1] around advice 1[Aspect1] before advice OK[Aspect1] around advice2[Aspect1] after advice[Aspect1] afterReturning advice[Aspect2] around advice2[Aspect2] afterReturning advice
But at this time, I can't conclude that aspect2 is definitely executed before aspect1.
Don't believe it? You restart the server and try again, maybe you will see the following execution results:
[Aspect1] around advice 1[Aspect1] before advice[Aspect2] around advice 1[Aspect2] before advice OK[Aspect2] around advice2[Aspect2] after advice[Aspect2] afterReturning advice[Aspect1] around advice2[Aspect1] afterReturning advice
That is, in this case, the execution order of aspect1 and aspect2 is unknown. So how to solve it? No hurry, the solution will be given below.
Test exceptions
Directly enter the following URL in the browser and press Enter: http://192.168.142.8:7070/aoptest/v1/aop/test?throwException=true 1
We will see the output result:
[Aspect2] around advice 1[Aspect2] before advice[Aspect1] around advice 1[Aspect1] before advicethrowing an exception[Aspect1] after Throwing advice[Aspect2] after Throwing advice
Similarly, if you restart the server and then test it again, you may see the following results:
[Aspect1] around advice 1[Aspect1] before advice[Aspect2] around advice 1[Aspect2] before advicethrowing an exception[Aspect2] afterThrowing advice[Aspect1] afterThrowing advice
That is to say, in the same way, the execution order of aspect1 and aspect2 is also undetermined in exceptional circumstances.
So in case 2, how do you specify the execution order of each aspect?
There are two methods:
No matter which method is used, the smaller the aspect, the more it is executed first.
For example, we add @Order annotations for apsect1 and aspect2 respectively, as follows:
@Order(5)@Component@Aspectpublic class Aspect1 { // ...}@Order(6)@Component@Aspectpublic class Aspect2 { // ...} After this modification, it can be ensured that in any case, the advice in aspect1 is always executed before the advice in aspect2. As shown in the figure below:
Note
If two identical advices are defined for the same pointcut (for example, two @Before) are defined, then the execution order of these two advices cannot be determined, even if you add the @Order annotation to these two advices, it will not work. Remember this.
For the @Around advice, regardless of whether it has a return value or not, it must call pjp.proceed() inside the method; otherwise, the interface in the Controller will not be executed, which will also cause the @Before advice to not be triggered. For example, we assume that under normal circumstances, the execution order is "aspect2 -> apsect1 -> controller". If we delete pjp.proceed(); in @Around in aspect1, then the output we see will be:
[Aspect2] around advice 1[Aspect2] before advice[Aspect1] around advice 1[Aspect1] around advice2[Aspect1] after advice[Aspect1] afterReturning advice[Aspect2] around advice2[Aspect2] afterReturning advice
From the results, we can find that the interface in Controller has not been executed, and @Beforeadvice in aspect1 has not been executed either.
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.