Spring AOP use articles: familiar with the use of pre-notification, post-notification, return notification, exception notification, and understand its related features

Spring AOP use articles: familiar with the use of pre-notification, post-notification, return notification, exception notification, and understand its related features

Preface

  • This time I will summarize 5 knowledge points about spring aop, mainly centering on five topics : AOP usage, AOP principle, transaction usage, transaction principle, transaction synchronizer usage .

  • The principle of AOP is divided into two topics:

    1. How to bind the various notifications we defined with the target method in the source code 2. What is the execution order of our aop proxy object? Copy code
  • The principle of transaction is mainly based on a case of transaction propagation mechanism to explain the execution process of spring transaction at the bottom.

  • And the core of this summary is: how to use Spring AOP, and understand related

    Notice
    Features (related concepts in which no longer tired, and we can refer to other articles), ado, we just start it!

1. Test case

1.1 Preview test case project structure

  • Entry.java of project startup entry class:

    public class Entry { public static void main (String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); ObjectService objectService = context.getBean(ObjectService.class); objectService.list( "hi" ); } //Configure the scan class and start the aop function @Configuration @ComponentScan("com.eugene.sumarry.aop.csdn") @EnableAspectJAutoProxy(proxyTargetClass = false, exposeProxy = true) public static class AppConfig { } //The method identified by this annotation will be enhanced @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface AspectAnnotation { } } Copy code
  • Interface class ObjectService.java

    public interface ObjectService { String[] list(String str); String findOne () ; } Copy code
  • Interface implementation class ObjectServiceImpl.java

    @Service public class ObjectServiceImpl implements ObjectService { @Entry .AspectAnnotation @Override public String[] list(String str) { return new String[] {str, "avenger" , "eug" }; } @Override public String findOne () { return "avengerEug" ; } } Copy code
  • AspectDefinition.java

    @Aspect @Component public class AspectDefinition { /** * Only methods that include @AspectAnnotation annotations will be enhanced */ @Pointcut("@annotation(com.eugene.sumarry.aop.csdn.Entry.AspectAnnotation)") public void pointcutAnnotation () { } /** * Pre-notification: trigger before executing the target method */ @Before(value = "pointcutAnnotation()") public void methodBefore (JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); System.out.println( "Pre-notification: method name:" + methodName + ", parameter" + Arrays.asList(joinPoint.getArgs())); } /** * Post notification: triggered after executing the target method */ @After(value = "pointcutAnnotation()") public void methodAfter (JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); System.out.println( "Post notification: method name:" + methodName + ", parameter" + Arrays.asList(joinPoint.getArgs())); } /** * Return notification: Triggered after the target method is executed and the parameters are returned. */ @AfterReturning(value = "pointcutAnnotation()", returning = "result") public void methodAfterReturning (JoinPoint joinPoint, Object result) { String methodName = joinPoint.getSignature().getName(); System.out.println( "Return notification: method name:" + methodName + "," + "parameter" + Arrays.asList(joinPoint.getArgs()) + "," + "return result:" ); if (result instanceof String[]) { Arrays.stream((String[]) result).forEach(System.out::println); } else { System.out.println(result); } } /** * Exception notification: triggered after the target method throws an exception */ @AfterThrowing(value = "pointcutAnnotation()", throwing="ex") public void methodExceptionOccurred (JoinPoint joinPoint, Exception ex) { String methodName = joinPoint.getSignature().getName(); System.out.println( "Exception notification: method name:" + methodName + ", parameter" + Arrays.asList(joinPoint.getArgs()) + ", exception information:" + ex.getMessage()); } } Copy code

1.2 Analysis of test case project structure

  • The entire project structure uses the java config method to start spring. In the AppConfig class, the core functions of the spring project are defined:

    1. The @Configuration annotation identifies AppConfig as a configuration class

    2. @ComponentScan specifies the package path scanned by spring as: com.eugene.sumarry.aop.csdn , which will indicate that all annotations recognized by spring under this package will be parsed

    3. The @EnableAspectJAutoProxy annotation indicates that the spring aop-related components are imported, which means that the aop function is activated. Among them, the proxyTargetClass attribute specifies which proxy method aop uses to enhance the target class, and its rules are as follows:

    proxyTargetClassmeaning
    The default is falseThe jdk dynamic proxy is used, and finally the proxy object and the target object will implement the same interface
    Set to trueLook at the structure of the target class. If the target class is an interface, the jdk dynamic proxy is used here. Otherwise, use cglib proxy. Follow-up will be
    Principles of AOP
    Proof

    And the exposeProxy is set to true, the spring will be exposed to the thread proxy object in ThreadLocal variable , which specific scene, you can read on.

    In the aspect definition class AspectDefinition , we define an aspect, and the point of cut is: if a method in a bean in spring is marked by the **@AspectAnnotation** annotation, then the corresponding method of the bean will be enhanced. And the enhanced logic includes four parts : pre-notification, post-notification, return notification, and exception notification . The use of notification here may not be very vivid. You can understand it as four hook functions, which correspond to the hook functions. The trigger timing and characteristics are shown in the following table (also can refer to the definition in the AspectDefinition class):

    Notification type (hook function)Trigger timingFeature
    Advance noticeTriggered before calling the target methodAs long as the program has no serious problems and pre-notification is defined, it will definitely trigger
    Post notificationTriggered after the target method logic is executed, or an exception is thrown inside the target methodAs long as the program has no serious problems and a post notification is defined, it will definitely trigger
    Return notificationTriggered when the return value of the target method returnsMutually exclusive with exception notifications
    Exception notificationTriggered when an exception occurs during the execution of the target methodMutually exclusive with the return notification , one mountain cannot tolerate two tigers

    According to the above introduction, we can easily find that for the ObjectServiceImpl class, the list method has been enhanced, but the findOne method has not been enhanced. This means that when the list method is executed, two things will happen. Case 1 : Trigger

    Pre-notification, post-notification, return notification
    . Situation 2 :
    Pre-notification, post-notification, exception notification
    . The reasons for the mutual exclusion of exception notifications and return notifications will be discussed in the follow-up
    Principles of AOP
    Proof.

1.3 Test the three call situations of the list method

1.3.1 No exception is thrown inside the list method of the target object

  • Run the main method of the Entry.java class directly, and get the following results

  • Only trigger when the target method list is thrown out without exception

    Pre-notification, post-notification, return notification
    , In line with the first case of the above analysis .

1.3.2 An exception is thrown inside the list method of the target object

  • Change the list method to something like this:

    @Entry .AspectAnnotation @Override public String [] List (String STR) { int X = . 1/0 ; return new new String [] {STR, "Avenger" , "EUG" }; } Copy code
  • Run the main method of the Entry.java class directly, and get the following results

  • When the target method list has an exception and throws out, only trigger

    Pre-notification, post-notification, exception notification
    , In line with the second case of the above analysis . The application program also proved that the return notification and the exception notification are mutually exclusive and cannot exist at the same time. (The follow-up will be in
    Principles of AOP
    Proof
    )

1.3.3 Call another enhanced method in the list method of the target object

  • Transform the ObjectServiceImpl.java class above and add the findOne method

    @Entry.AspectAnnotation
    Note, the reconstructed structure is as follows:

    @Service public class ObjectServiceImpl implements ObjectService { @Entry .AspectAnnotation @Override public String[] list(String str) { //How to call the findOne method with enhanced functions here? return new String[] {str, "avenger" , "eug" }; } //Add @Entry.AspectAnnotation annotation @Entry .AspectAnnotation @Override public String findOne () { return "avengerEug" ; } } Copy code
  • As noted above: How to call the findOne method with enhanced functions in the list method? Use directly

    this.findOne
    does it work? It can be clearly informed here: the use of this.findOne will definitely not be able to call the findOne method with enhanced functions. This involves some scenarios where aop fails. If you want to understand these scenarios, you must understand the implementation principle of aop. Here, I will write a separate article about AOP failure scenarios to explain. So what are the ways to achieve this demand?

Method 1: Depend on yourself and inject yourself
  • As shown in the following code:

    @Service public class ObjectServiceImpl implements ObjectService { //I rely on injecting myself, at this time, I inject a proxy object @Autowired ObjectService objectService; @Entry .AspectAnnotation @Override public String[] list(String str) { objectService.findOne(); return new String[] {str, "avenger" , "eug" }; } @Entry .AspectAnnotation @Override public String findOne () { return "avengerEug" ; } } Copy code
Method 2: Use the spring context to reacquire the bean
  • As shown in the following code:

    @Service public class ObjectServiceImpl implements ObjectService , ApplicationContextAware { //Use spring context. Or there are many SpringContextUtils tool classes on the Internet, you can use them, but the principle is the same, they all implement the ApplicationContextAware interface ApplicationContext applicationContext; @Override public void setApplicationContext (ApplicationContext applicationContext) throws BeansException { this .applicationContext = applicationContext; } @Entry .AspectAnnotation @Override public String[] list(String str) { applicationContext.getBean(ObjectService.class).findOne(); return new String[] {str, "avenger" , "eug" }; } @Entry .AspectAnnotation @Override public String findOne () { return "avengerEug" ; } } Copy code
Method 3: Use the exposeProxy attribute annotated with @EnableAspectJAutoProxy
  • The exposeProxy property is introduced above. If you set exposeProxy to true, Spring will expose the proxy object to the thread variable. Therefore, when exposeProxy is set to true, continue to modify the ObjectServiceImpl class to achieve the need to call the enhanced method findOne within the enhanced method list

  • The code modification is as follows:

    @Service public class ObjectServiceImpl implements ObjectService { @Entry .AspectAnnotation @Override public String[] list(String str) { ((ObjectService) AopContext.currentProxy()).findOne(); return new String[] {str, "avenger" , "eug" }; } @Entry .AspectAnnotation @Override public String findOne () { return "avengerEug" ; } } Copy code

    Use AopContext.currentProxy() api to get the proxy object exposed by spring in thread variables. Since the proxy object exists in the thread variable, that is, in ThreadLocal, we need to ensure that the entire call chain will not switch threads, otherwise the proxy object exposed by spring will not be obtained.

Regardless of the above method, the final execution result is this:

After triggering the pre-notification of the list method, the findOne method is executed, which indirectly indicates that the pre-notification is triggered before the execution of the target method , because findOne is executed inside the target method list, so the logic of the target method list The execution has not yet been completed, and the post notification, return notification or exception notification of the list method will not be triggered until the logic of the findOne method has been executed. The calling process is shown in the following figure:

1.4 Based on 1.3.3 Method 3, test several situations where the list method throws exceptions

1.4.1 Throw an exception before calling the findOne method

  • Transform the ObjectServiceImpl.java class:

    @Service public class ObjectServiceImpl implements ObjectService { @Entry .AspectAnnotation @Override public String[] list(String str) { //An exception is thrown here, and the findOne method has not been called int x = 1/0 ; ((ObjectService) AopContext.currentProxy()).findOne(); return new String[] {str, "avenger" , "eug" }; } @Entry .AspectAnnotation @Override public String findOne () { return "avengerEug" ; } } Copy code
  • The result of its operation is:

    Obviously: From the above conclusion, we can know that throwing an exception before calling the findOne method only affects the list method. This is also easier to understand, because I have not yet called the findOne method, and of course it will not affect the findOne method.

1.4.2 Throw an exception after calling the findOne method

  • Transform the ObjectServiceImpl.java class:

    @Service public class ObjectServiceImpl implements ObjectService { @Entry .AspectAnnotation @Override public String[] list(String str) { ((ObjectService) AopContext.currentProxy()).findOne(); After calling method findOne//Throws int X = . 1/0 ; return new new String [] {STR, "Avenger" , "EUG" }; } @Entry .AspectAnnotation @Override public String findOne () { return "avengerEug" ; } } Copy code
  • The results of its execution are as follows:

    Obviously, throwing an exception after the execution of the findOne method did not affect the execution of the findOne method. Take the flow chart we drew before to illustrate:

    as the picture shows,**

    Continue to execute the remaining logic of the target method list
    ** An exception was thrown at the node, which is the above
    int x = 1/0;
    This code, it only affects the logic of the list method, and will no longer affect the findOne method, because the enhanced logic of the findOne method has been executed.

  • To add an extra word: If you have a better understanding of the REQUIRES_NEW propagation mechanism of Spring transactions , this situation should also occur. Suppose our list is the default propagation mechanism REQUIRED , and findOne is the REQUIRES_NEW propagation mechanism. Then in this case, the findOne transaction will be committed, and the list transaction will be rolled back. The principle is consistent with the above analysis, because findOne is a REQUIRES_NEW propagation mechanism. It will additionally create a new transaction and throw an exception when it continues to execute the remaining logic of the list after the findOne transaction is submitted. At this time, the findOne transaction has been submitted, of course Will not affect find1. so only the list method is affected at this time.

1.4.3 When the findOne method is called, an exception is thrown inside the findOne method

  • Transform the ObjectServiceImpl.java class:

    @Service public class ObjectServiceImpl implements ObjectService { @Entry .AspectAnnotation @Override public String[] list(String str) { ((ObjectService) AopContext.currentProxy()).findOne(); return new String[] {str, "avenger" , "eug" }; } @Entry .AspectAnnotation @Override public String findOne () { //Throws here int X = . 1/0 ; return "avengerEug" ; } } Copy code
  • The results of its operation are as follows:

    According to the results, it can be seen that it affects the list and findOne methods, that is to say, the exceptions thrown inside the findOne method will affect the methods on the entire call chain, and then trigger the exception notifications of the list and findOne. I don t know if the friends here have any feelings: this is very similar to the execution process of the responsibility chain. If we do not do additional exception capture for each node on the chain, as long as one chain throws an exception, It will throw the anomaly upward layer by layer. Fortunately, the design pattern of chain of responsibility is used in the invocation process of Spring's AOP. We will explain in the next article on AOP principles .

  • To add an extra word: If you have a better understanding of the REQUIRED propagation mechanism of Spring transactions , there should also be cases where internal method throwing exceptions will eventually roll back the entire call chain. This situation is consistent with the principle of the use case we are testing now, because the two methods use the same transaction, and eventually the exception will be thrown to the upper layer. When the transaction manager senses that the exception occurs, The transaction manager will do a rollback operation.

2. summary

  • Through this study, we have learned that the pre-notification, post-notification, return notification, and exception notification are actually callbacks of a hook function, and for
    Return notification and exception notification are mutually exclusive
    as well as
    How does Spring determine which proxy method to use to generate proxy objects
    Questions, I will be in the follow-up AOP principle article prove .
  • In the 1.3 and 1.4 chapters, we tested the various call situations and exceptions of aop, and also mentioned some knowledge points about transactions. If this is not clear, we will follow up in the transaction principle chapter. explain in detail.
  • Source link: click to view
  • If you find my article useful, please like, bookmark and follow. :laughing:
  • I'm a slow walker, but I never walk backwards