1, Aop
Aspect Oriented Programming is a supplement to object-oriented thinking.
Aspect oriented programming is to dynamically enhance the function of the method without changing the program source code when the program is running. There are many common use scenarios:
- journal
- Transaction -- common code extraction
- Database operation
- ...
The above operations have a lot of templated code
Common AOP concepts
Pointcut: the place where you want to add code is called a pointcut
Notifications (enhanced): notifications are code that is dynamically added to pointcuts
Cut: cut point + notification
Connection point: definition of tangent point
AOP implementation
Aop is actually implemented based on Java's dynamic proxy
There are two ways to implement dynamic proxy in Java: cglib and jdk
2, Dynamic agent example
- Define a calculator interface
package org.kk.aop; /** * @program: ioc01 * @description:Implement dynamic agent and define a calculator interface * @author: zjx * @create: 2021-12-27 22:38 **/ public interface MyCalculator { int add(int a,int b); }
- Implementation interface
MyCalculatorImpl is the implementation class of the interface
package org.kk.aop; /** * @program: ioc01 * @description: An implementation of calculator interface * @author: zjx * @create: 2021-12-27 22:44 **/ public class MyCalculatorImpl implements MyCalculator{ @Override public int add(int a, int b) { return a+b; } }
- Implementation of Proxy class using Proxy class
The above entity is processed by proxy class and returns a new entity (added method)
package org.kk.aop; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * @program: ioc01 * @description: Enhancements in proxy classes * @author: zjx * @create: 2021-12-27 22:45 **/ public class CalculatorProxy { public static Object getInstance(MyCalculatorImpl myCalculator) { //The first parameter is classLoader. The second parameter implements the interface. The third parameter is new InvocationHandler return Proxy.newProxyInstance(CalculatorProxy.class.getClassLoader(), myCalculator.getClass().getInterfaces(), new InvocationHandler() { @Override /** * @Description: * @Param: [proxy:Proxy object, method: proxy method (add), args method parameter] * @return: java.lang.Object Method * @Author: zjx * @Date: 2021/12/27 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before method execution"); Object invoke = method.invoke(myCalculator, args); System.out.println("After method execution"); return invoke; //You can also return 99, and the result of calling add is 99 } }); } }
- Call to obtain an object processed by a proxy class
public class Main { @Test public void test1() { MyCalculatorImpl myCalculator=new MyCalculatorImpl(); MyCalculator calculator = ((MyCalculator) CalculatorProxy.getInstance(myCalculator)); int add=calculator.add(3,4); System.out.println("add="+add); } }
Call result:
Before method execution After method execution add=7
- summary
The principle of Aop is the same as that of the above dynamic proxy. There is no need to change the original code (Impl) and add new methods (through proxy classes)
3, Aop five notifications
Aop has five types of notifications:
In the above code, in method Execute before invoke - pre notification, in method Execute after invoke - post notification, exception notification, return notification, surround notification
1. Add 3 dependencies
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.19.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.7</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.9.7</version> </dependency> </dependencies>
2. Define tangent point
There are two ways: using annotations (not recommended) and using rules
Using annotations
- Custom Action annotation
package org.kk.aop; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Action { }
- Add custom annotations to methods in Impl
package org.kk.aop; import org.springframework.stereotype.Component; /** * @program: ioc01 * @description: An implementation of calculator interface * @author: zjx * @create: 2021-12-27 22:44 **/ @Component public class MyCalculatorImpl implements MyCalculator{ @Override @Action //Add this custom annotation to the method that needs to be enhanced, intrusively -- change the source code public int add(int a, int b) { return a+b; } @Override public void min(int a, int b) { System.out.println(a+"-"+b+"="+(a-b)); } //Then I want to add a log to these two methods without changing the source code }
- Create a new class to define notifications
3. New section
@Component
@Aspect
The method is used to notify: pre notify the package path of @ Before("@annotation (org.javaboy.app.Action)") annotation
package org.kk.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; /** * @program: ioc01 * @description:Define enhancements and notifications * @author: zjx * @create: 2021-12-28 13:46 **/ @Component @Aspect //Indicates that this is a faceted five notifications public class LogAspect { /** * @Description: Pre notification, which is executed before the start of the add logic * @Param: [joinPoint] * @return: void * @Author: zjx * @Date: 2021/12/28 */ @Before("@annotation(Action)")//There will be a pre notification before the method with Action annotation is executed public void before(JoinPoint joinPoint) { String name=joinPoint.getSignature().getName(); System.out.println(name+"The method begins to execute"); } }
To add annotations to the configuration class, you need to turn on the automatic proxy
package org.kk.aop; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.stereotype.Component; /** * @program: ioc01 * @description: * @author: zjx * @create: 2021-12-28 13:56 **/ @Configuration @ComponentScan //Also turn on automatic proxy @EnableAspectJAutoProxy public class JavaConfig { }
test
@Test public void test2() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class); MyCalculator bean = context.getBean(MyCalculator.class); int res = bean.add(3, 4); System.out.println(res); }
result
add The method begins to execute 7
Four notification methods
package org.kk.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; /** * @program: ioc01 * @description:Define enhancements and notifications * @author: zjx * @create: 2021-12-28 13:46 **/ @Component @Aspect //Indicates that this is a faceted five notifications public class LogAspect { /** * @Description: Pre notification, which is executed before the start of the add logic * @Param: [joinPoint] * @return: void * @Author: zjx * @Date: 2021/12/28 */ @Before("@annotation(Action)")//There will be a pre notification before the method with Action annotation is executed public void before(JoinPoint joinPoint) { String name=joinPoint.getSignature().getName(); System.out.println(name+"The method begins to execute"); } //Post notification @After("@annotation(Action)") public void after(JoinPoint joinPoint) { String name=joinPoint.getSignature().getName(); System.out.println(name+"Method execution is over"); } //Return notification /** * @Description: Return notification. You can get the return value of the target method in this method. If the return value of the target method is void, you will receive null * @Param: [joinPoint, r The returned parameter name corresponds to the parameter name of the method here] * @return: void * @Author: zjx * @Date: 2021/12/28 */ @AfterReturning(value = "@annotation(Action))",returning = "r") public void returning(JoinPoint joinPoint,Integer r)//If the type does not match the return type of the method, the result will not be returned; You can use Object { String name=joinPoint.getSignature().getName(); System.out.println(name+"Return notification:"+r);//r is the result of method execution } /** * @Description: Exception notification. When the target method throws an exception, the method will be triggered * @Param: [joinPoint, e The exception parameter corresponds to the parameter name of the method one by one. Note the type of exception] * @return: void * @Author: zjx * @Date: 2021/12/28 */ @AfterThrowing(value = "@annotation(Action)",throwing = "e") public void afterThrowing(JoinPoint joinPoint,Exception e) { String name=joinPoint.getSignature().getName(); System.out.println(name+"Method exception notification:"+e.getMessage()); } /** * @Description: Surround notification is the integration of the above four methods. The core of surround notification is similar to executing methods in reflection * @Param: [pjp] * @return: java.lang.Object * @Author: zjx * @Date: 2021/12/28 */ @Around("@annotation(Action)") public Object around(ProceedingJoinPoint pjp) throws Throwable { //This is a bit similar to method In the invoke method, we can add logs before and after this method, which is equivalent to pre - and post notifications Object proceed=pjp.proceed(new Object[]{5,5}); return proceed; } }
//Optimization 1 defines the pointcut through the method, and the following annotations are changed to @ Before("pointcut()") @Pointcut("@annotation(Action)") public void pointcut() { }
Non intrusive definition of tangent point:
4. It is recommended to use @ Pointcut annotation to define Pointcut
//Noninvasive definition of tangent point @Pointcut("execution(* org.kk.aop.service.*.*(..))") //Any class, any method parameter type, any number public void pointcut() { } package org.kk.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; /** * @program: ioc01 * @description:Define enhancements and notifications -- defined aspects * @author: zjx * @create: 2021-12-28 13:46 **/ @Component @Aspect //Indicates that this is a faceted five notifications public class LogAspect { // //Optimization 1 defines the pointcut through the method, and the following annotations are changed to @ Before("pointcut()") // @Pointcut("@annotation(Action)") // public void pointcut() // { // // } //Noninvasive definition of tangent point @Pointcut("execution(* org.kk.aop.service.*.*(..))") //Any class, any method parameter type, any number public void pointcut() { } /** * @Description: Pre notification, which is executed before the start of the add logic * @Param: [joinPoint] * @return: void * @Author: zjx * @Date: 2021/12/28 */ @Before("pointcut()")//There will be a pre notification before the method with Action annotation is executed public void before(JoinPoint joinPoint) { String name=joinPoint.getSignature().getName(); System.out.println(name+"The method begins to execute"); } //Post notification @After("pointcut()") public void after(JoinPoint joinPoint) { String name=joinPoint.getSignature().getName(); System.out.println(name+"Method execution is over"); } //Return notification /** * @Description: Return notification. You can get the return value of the target method in this method. If the return value of the target method is void, you will receive null * @Param: [joinPoint, r The returned parameter name corresponds to the parameter name of the method here] * @return: void * @Author: zjx * @Date: 2021/12/28 */ @AfterReturning(value = "pointcut())",returning = "r") public void returning(JoinPoint joinPoint,Integer r)//If the type does not match the return type of the method, the result will not be returned; You can use Object { String name=joinPoint.getSignature().getName(); System.out.println(name+"Return notification:"+r);//r is the result of method execution } /** * @Description: Exception notification. When the target method throws an exception, the method will be triggered * @Param: [joinPoint, e The exception parameter corresponds to the parameter name of the method one by one. Note the type of exception] * @return: void * @Author: zjx * @Date: 2021/12/28 */ @AfterThrowing(value = "pointcut()",throwing = "e") public void afterThrowing(JoinPoint joinPoint,Exception e) { String name=joinPoint.getSignature().getName(); System.out.println(name+"Method exception notification:"+e.getMessage()); } /** * @Description: Surround notification is the integration of the above four methods. The core of surround notification is similar to executing methods in reflection * @Param: [pjp] * @return: java.lang.Object * @Author: zjx * @Date: 2021/12/28 */ @Around("pointcut()") public Object around(ProceedingJoinPoint pjp) throws Throwable { //This is a bit similar to method In the invoke method, we can add logs before and after this method, which is equivalent to pre - and post notifications Object proceed=pjp.proceed(new Object[]{5,5}); //return 10 return proceed; } }
xml configuration Aop
<?xml version="1.0" encoding="UTF-8"?> <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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <bean class="org.kk.aop.LogAspect" name="logAspect"/> <bean class="org.kk.aop.service.MyCalculatorImpl" name="myCalculator"/> <aop:config> <aop:pointcut id="pointcut" expression="execution(* org.kk.aop.service.*.*(..))"/> <aop:aspect ref="logAspect"> <aop:before method="before" pointcut-ref="pointcut"/> <aop:before method="after" pointcut-ref="pointcut"/> </aop:aspect> </aop:config> </beans>