0. Important concepts of Spring AOP
oop is impeccable for business abstraction and encapsulation, but some system-level requirements such as system logs, performance statistics, etc. are scattered in all corners of the software, and it is very unpleasant to maintain. The solution to this problem is indeed beyond the power of oop and
1. Notification: a method that will be executed before and after the target method is executed
@After(value="execution(* cn.fww.dao.UserDao.addUser(..))") public void log(){ System.out.println("log"); }
This method is notification. The target method is addUser() of the UserDao class. The log method is executed after addUser is executed, so the log method is a post-notification, which is indicated by adding the @After annotation to the method.
According to the execution order of the notification and target method, we can divide the notification into five types:
- Pre-notification (before): Executed before the target method is executed.
- Post notification (after): Executed after the target method is executed
- After returning advice (after returning): Execute after the target method returns, execute the post-advice first and then execute the post-return advice.
- Exception notification (after throwing): executed when the target method throws an exception
- Around advice (around): execute in the target function execution
//The order of execution of these four notifications is as follows: try{ try{ //@Before prepend method.invoke(..); }finally{ //@After post } //@AfterReturning post return }catch(){ //@AfterThrowing throws notification }
1.1 JoinPoint object
The JoinPoint object encapsulates the information of the aspect method in SpringAop. By adding the JoinPoint parameter to the aspect method, you can get the JoinPoint object that encapsulates the method information.
- JoinPoint object
method name | Function |
---|---|
Signature getSignature(); | Get the object that encapsulates the signature information, in which you can get the target method name, the Class of the class to which it belongs, and other information |
Object[] getArgs(); | Get the parameter object passed into the target method |
Object getTarget(); | get the proxied object |
Object getThis(); | get the proxy object, |
- ProceedingJoinPoint object
The ProceedingJoinPoint object is a sub-interface of JoinPoint, which is only used in the aspect method of @Around,
The following two methods were added.
Object proceed() throws Throwable //Execute the target method Object proceed(Object[] var1) throws Throwable //The new parameters passed in to execute the target method
1.2 Pre-notification @Before
The pre-advice is annotated by the @Before annotation, and the value of the pointcut expression can be directly passed in. The advice is executed before the target function is executed. Note that JoinPoint is a static variable provided by Spring. Through the joinPoint parameter, you can get the target object's Information, such as class name, method parameters, method name, etc., this parameter is optional
@Aspect @Component public class aopAspect { /** * Defines a pointcut expression that determines which classes require proxies * execution(* aopdemo.*.*(..))All methods representing all classes under the aopdemo package will be proxied */ @Pointcut("execution(* aopdemo.*.*(..))") public void declareJoinPointerExpression() {} /** * Pre-method, executed before the target method is executed * @param joinPoint The object that encapsulates the proxy method information. If it is not used, it can be ignored and not written. */ @Before("declareJoinPointerExpression()") public void beforeMethod(JoinPoint joinPoint){ System.out.println("The target method is named:" + joinPoint.getSignature().getName()); System.out.println("The simple class name of the class to which the target method belongs:" + joinPoint.getSignature().getDeclaringType().getSimpleName()); System.out.println("The class name of the class to which the target method belongs:" + joinPoint.getSignature().getDeclaringTypeName()); System.out.println("target method declaration type:" + Modifier.toString(joinPoint.getSignature().getModifiers())); //Get the parameters passed to the target method Object[] args = joinPoint.getArgs(); for (int i = 0; i < args.length; i++) { System.out.println("the first" + (i+1) + "parameters are:" + args[i]); } System.out.println("proxied object:" + joinPoint.getTarget()); System.out.println("proxy object itself:" + joinPoint.getThis()); } }
1.3 Surround Notification @Around
Surround advice can be executed before or after the target method. More importantly, the surround advice can control whether the target method is directed to execution, but even so, we should try to meet the requirements in the simplest way, only in the When executing before the target method, you should use pre-advice instead of surround advice. The case code is as follows. The first parameter must be ProceedingJoinPoint. The target function is executed through the proceed() method of the object. The return value of proceed() is the return value of the surround notification. Similarly, the ProceedingJoinPoint object can also obtain information about the target object, such as class name, method parameters, method name and so on.
@Aspect @Component public class aopAspect { /** * Defines a pointcut expression that determines which classes require proxies * execution(* aopdemo.*.*(..))All methods representing all classes under the aopdemo package will be proxied */ @Pointcut("execution(* aopdemo.*.*(..))") public void declareJoinPointerExpression() {} /** * Surround method, you can customize the timing of target method execution * @param pjd JoinPoint The subinterface of , added * Object proceed() throws Throwable Execute the target method * Object proceed(Object[] var1) throws Throwable The new parameters passed in to execute the target method * two methods * @return This method requires a return value, and the return value is regarded as the return value of the target method */ @Around("declareJoinPointerExpression()") public Object aroundMethod(ProceedingJoinPoint pjd){ Object result = null; try { //advance notice System.out.println("Before the target method is executed..."); //Execute the target method //result = pjd.proeed(); //Execute the target method with the new parameter values result = pjd.proceed(new Object[]{"newSpring","newAop"}); //Back to Notification System.out.println("After the target method returns the result..."); } catch (Throwable e) { //exception notification System.out.println("After executing the target method exception..."); throw new RuntimeException(e); } //post notification System.out.println("After the target method is executed..."); return result; } }
1.4 Post return notification @AfterReturning
Annotated with the @AfterReturning annotation, the function is executed after the target function is executed, and the final return value returnVal of the target function can be obtained. When the target function has no return value, returnVal will return null, which must be annotated with returning = “returnVal” Specify the name of the parameter and must be the same as the parameter name of the notification function. Please note that these parameters are optional in any notification. You can fill in them directly when you need to use them. When you don't need to use them, you can complete them without declaring them. as follows
/** * Post notification, can not be provided when no parameter is required */ @AfterReturning(value="execution(* com.zejian.spring.springAop.dao.UserDao.*User(..))") public void AfterReturning(){ System.out.println("I am post notification..."); } /** * post notification * returnVal,The return value of the pointcut method after execution */ @AfterReturning(value="execution(* com.zejian.spring.springAop.dao.UserDao.*User(..))",returning = "returnVal") public void AfterReturning(JoinPoint joinPoint,Object returnVal){ System.out.println("I am post notification...returnVal+"+returnVal); }
1.5 Exception notification @AfterThrowing
The notification will only be triggered when an exception occurs, and a variable that receives exception information is declared by throwing. The same exception notification is also used for the Joinpoint parameter, which can be added when needed, as follows:
/** * throw notification * @param e information about the exception thrown */ @AfterThrowing(value="execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))",throwing = "e") public void afterThrowable(Throwable e){ System.out.println("Abnormal:msg="+e.getMessage()); }
1.6 Post notification @After
This notification is somewhat similar to a finally block and will be executed regardless of the circumstances as long as it is applied.
/** * A method that will be executed no matter what * joinPoint parameter */ @After("execution(* com.zejian.spring.springAop.dao.UserDao.*User(..))") public void after(JoinPoint joinPoint) { System.out.println("final notice...."); }
2. Define the pointcut function: the target method for application notification enhancement
2.1 Pass the pointcut function directly as a value to the advice type
The problem we are facing now is how to describe the target method that needs to be enhanced. If only a specific method needs to be enhanced, it is easy to find it through the class name and method name, but often many methods in real requirements need the same notification. To enhance, Spring AOP provides us with a syntax for describing methods such as in the above example:
@After(value="execution(* cn.fww.dao.UserDao.addUser(..))") //The pointcut expression defined by the execution keyword //Directly pass the execution defined match expression as a value to the notification type
The above code is used to describe the method that needs to apply the notification. The meaning here is that the parameters in the UserDao class of the cn.fww.dao package are arbitrary, and the return value of the addUser() method is arbitrary.
2.2 via @pointcut
@Component @Aspect public class AspectDef { //@Pointcut("execution(* com.test.spring.aop.pointcutexp..JoinPointObjP2.*(..))") //@Pointcut("within(com.test.spring.aop.pointcutexp..*)") //@Pointcut("this(com.test.spring.aop.pointcutexp.Intf)") //@Pointcut("target(com.test.spring.aop.pointcutexp.Intf)") //@Pointcut("@within(org.springframework.transaction.annotation.Transactional)") //@Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)") @Pointcut("args(String)") public void pointcut1() { } @Before(value = "pointcut1()") public void beforeAdvice() { System.out.println("pointcut1 @Before..."); }
2.3execution basic format
The execution format is:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)
returning type pattern,name pattern, parameters pattern is required.
- ret-type-pattern: It can be a class name that represents any return value, full path, etc.
- name-pattern: Specify the method name, representing so, set, representing all methods starting with set.
- parameters pattern: specifies the method parameters (declared type), (...) represents all parameters, () represents a parameter
- (*, String) means that the first parameter is any value, and the second is of type String.
3. Connection points: A connection point is a method that can be enhanced with notifications
Because Spring Aop can only be enhanced for methods, the join point here refers to the method, and once the join point is enhanced, it becomes the pointcut.
public void addUser(){ System.out.println("Add user"); } public void updateUser(){ System.out.println("modify user"); } public void deleteUser(){ System.out.println("delete users"); } All three methods are connection points.
4. Aspects: a combination of pointcuts and notifications
@Aspect public class MyAspectLog { /** * Method to be executed after the method is executed */ @After(value="execution(* cn.fww.dao.UserDao.addUser(..))") public void log(){ System.out.println("log"); } }
In this aspect class contains both the pointcut addUser and the advice: log().
5.Aspect priority
Define multiple notifications in different aspects to respond to the same pointcut. When entering, the notification function in the aspect class with higher priority is executed first, and when exiting, it is executed last.
Define the AspectOne class and AspectTwo class as follows and implement the org.springframework.core.Ordered interface, which is used to control the priority of the aspect class, rewrite the getOrder method, and customize the return value. The smaller the return value (int type), the higher the priority. big. The return value of AspectOne is 0, and the return value of AspectTwo is 3. Obviously, AspectOne has a higher priority than AspectTwo.
@Aspect public class AspectOne implements Ordered { /** * Pointcut Define the pointcut function */ @Pointcut("execution(* com.zejian.spring.springAop.dao.UserDao.deleteUser(..))") private void myPointcut(){} @Before("myPointcut()") public void beforeOne(){ System.out.println("advance notice..AspectOne..Execution order 1"); } @Before("myPointcut()") public void beforeTwo(){ System.out.println("advance notice..AspectOne..Execution order 2"); } @AfterReturning(value = "myPointcut()") public void AfterReturningThree(){ System.out.println("post notification..AspectOne..Execution order 3"); } @AfterReturning(value = "myPointcut()") public void AfterReturningFour(){ System.out.println("post notification..AspectOne..Execution order 4"); } /** * Define the priority, the lower the value, the higher the priority * @return */ @Override public int getOrder() { return 0; } } //Aspect class AspectTwo.java @Aspect public class AspectTwo implements Ordered { /** * Pointcut Define the pointcut function */ @Pointcut("execution(* com.zejian.spring.springAop.dao.UserDao.deleteUser(..))") private void myPointcut(){} @Before("myPointcut()") public void beforeOne(){ System.out.println("advance notice....Execution order 1--AspectTwo"); } @Before("myPointcut()") public void beforeTwo(){ System.out.println("advance notice....Execution order 2--AspectTwo"); } @AfterReturning(value = "myPointcut()") public void AfterReturningThree(){ System.out.println("post notification....Execution order 3--AspectTwo"); } @AfterReturning(value = "myPointcut()") public void AfterReturningFour(){ System.out.println("post notification....Execution order 4--AspectTwo"); } /** * Define the priority, the lower the value, the higher the priority * @return */ @Override public int getOrder() { return 2; } }
6. Declared by way of configuration file
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--<context:component-scan base-package=""--> <!-- Define the target object --> <bean name="productDao" class="com.zejian.spring.springAop.dao.daoimp.ProductDaoImpl" /> <!-- Define the slice --> <bean name="myAspectXML" class="com.zejian.spring.springAop.AspectJ.MyAspectXML" /> <!-- configure AOP section --> <aop:config> <!-- Define the pointcut function --> <aop:pointcut id="pointcut" expression="execution(* com.zejian.spring.springAop.dao.ProductDao.add(..))" /> <!-- Define other pointcut functions --> <aop:pointcut id="delPointcut" expression="execution(* com.zejian.spring.springAop.dao.ProductDao.delete(..))" /> <!-- define notification order define priority,The smaller the value, the higher the priority--> <aop:aspect ref="myAspectXML" order="0"> <!-- define notification method Specify the notification method name,must be combined with MyAspectXML same in pointcut Specify the pointcut function --> <aop:before method="before" pointcut-ref="pointcut" /> <!-- post notification returning="returnVal" Define the return value must be the same as the name declared in the class--> <aop:after-returning method="afterReturn" pointcut-ref="pointcut" returning="returnVal" /> <!-- Surround Notification --> <aop:around method="around" pointcut-ref="pointcut" /> <!--exception notification throwing="throwable" Specifies the exception notification error message variable,Must be the same as the name declared in the class--> <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="throwable"/> <!-- method : method of notification(final notice) pointcut-ref : The pointcut method to which the notification applies --> <aop:after method="after" pointcut-ref="pointcut"/> </aop:aspect> </aop:config> </beans>