tips: This article starts with the official account of the official account. The source code is checked in the public address aop.
What is AOP?
AOP (aspect oriented programming) translates directly into aspect oriented programming. AOP is a programming idea and a supplement to object-oriented programming (OOP). Object oriented programming abstracts the program into objects at all levels, while aspect oriented programming abstracts the program into all aspects.
Why AOP?
In the actual development, we should all encounter such a scenario: how do we usually deal with a piece of repeated code between multiple modules?
In traditional process oriented programming, we will also abstract this code into a method, and then call this method where necessary. In this way, when this code needs to be modified, we only need to change this method. However, requirements always change. One day, when a new requirement is added and needs to be modified, we need to abstract a method and call this method separately where necessary, or we don't need this method, we still have to delete every place where the method is called. In fact, problems involving the same modifications in multiple places can be solved through AOP.
The essence of AOP
The essence of AOP is to modify the source code of multiple methods of business components by the AOP framework. You should understand that AOP is actually a typical application of agent mode.
According to the timing of modifying source code in AOP framework, it can be divided into two categories:
- For static AOP implementation, the AOP framework modifies the program source code at the compilation stage and generates static AOP proxy classes (the generated *. class file has been changed and a specific compiler needs to be used), such as AspectJ.
- For dynamic AOP implementation, the AOP framework dynamically generates proxy objects (JDK dynamic proxy in memory or CGlib dynamic generation of AOP proxy classes) at the runtime, such as spring AOP.
AOP terminology
Terms in AOP domain:
- Advice: enhanced processing in the AOP framework. The notification describes when and how the aspect is executed.
- Join point: a join point represents a point that can be inserted into the aspect during application execution. This point can be method call and exception throw. In Spring AOP, join points are always method calls.
- PointCut: you can insert a connection point for enhancement processing.
- Aspect: aspect is a combination of notification and pointcut.
- Introduction: introduction allows us to add new methods or properties to existing classes.
- Weaving: the process of adding enhancement processing to the target object and creating an enhanced object is weaving.
These terms are different from book translation. The key is to understand the meaning of each term in combination with the program.
The first Spring AOP project
New module
The name comes with your mood. This is spin AOP
to configure
In POM The following two dependencies are added to the XML file:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.14</version> </dependency> <!-- Add what you can provide AOP Dependency on annotation function, which is not Spring provide--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.3.14</version> </dependency>
Simulated service
1. Create a new package service and provide the following interfaces
public interface SomeService { void doSome(); void doSome(String name,Integer num); }
2. Under this package, create a new sub package impl and provide the following implementation classes
public class SomeServiceImpl implements SomeService { @Override public void doSome() { System.out.println("The original business method is in the implementation class"); } @Override public void doSome(String name, Integer num) { System.out.println("The original business method has two parameters in the implementation class:" + name + "->" + num); } }
3. Create a new package with any name. I call it handle here, and there is a class named MyAspect under it. The code is as follows:
@Aspect public class MyAspect { /** * execution() This is called tangent expression * <p> * The syntax is: Method modifier (can be omitted), method return type, package name full path of the method + method name + method parameter type list */ @Before(value = "execution(public void com.javafirst.service.impl.SomeServiceImpl.doSome(java.lang.String, java.lang.Integer))") public void aop_before() { System.out.println("Execute the logic before the original business method. Here is the code to be executed by the automatic proxy function."); } }
The @ Aspect annotation is used here, which is the dependency we added earlier, that is, the annotation provided by Spring. Here you need to know a concept: tangent expression
4. Spring core configuration file applicationcoext The XML code is as follows:
<?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"> <!-- Target object --> <bean id="someService" class="com.javafirst.service.impl.SomeServiceImpl"/> <!-- Section class --> <bean id="my_handle" class="com.javafirst.handle.MyAspect"/> <!-- Proxy generator --> <aop:aspectj-autoproxy proxy-target-class="true"/> </beans>
Here you will get a new label < aspect AutoProxy >, proxy generator, and the system will automatically generate proxy classes in memory without manually displaying write proxy classes.
5. As we learned in the previous section, the test code in this section will not change
/** * @Before Execute before target method */ @Test public void testBefore() { String config = "applicationContext.xml"; ApplicationContext context = new ClassPathXmlApplicationContext(config); SomeService someService = (SomeService) context.getBean("someService"); someService.doSome(); someService.doSome("No cliff", 87); }
6. Result display:
Execute the logic before the original business method. Here is the code to be executed by the automatic proxy function. In the implementation class of the original business method, there are two parameters: no child->87
Summary
Through the above process, we can briefly grasp the magic of implementing the proxy function through annotations. Of course, the pointcut expression mentioned earlier also has syntax, and the notification annotation in Spring AOP is more than @ Before. These two points are the focus of this article.
Then look down ↓
Spring AOP 5 notification annotations
Before learning the notification annotation, let's take a look at the tangent point expression mentioned earlier. Take the above example and make several deformations. You can test the results for easy understanding. It's not difficult.
- execution(public void com.javafirst.service.impl.SomeServiceImpl.doSome(java.lang.String, java.lang.Integer)): This indicates that the annotation function code is in the com javafirst. service. The doSome(java.lang.String, java.lang.Integer) method in the SomeServiceImpl class under the impl path is executed before.
- execution(void com.javafirst.service.impl.SomeServiceImpl.doSome(java.lang.String, java.lang.Integer)): Method modifiers can be omitted
- Execution (void com. Javafirst. Service. Impl. * doSome(java.lang.String, java.lang.Integer)): Specifies the doSome(java.lang.String, java.lang.Integer) methods of all classes under the package
- Execution (void com. Javafirst. Service. Impl. * * (java.lang.String, java.lang.Integer)): Specifies the method with two parameters (java.lang.String, java.lang.Integer) in all classes under the package
- Execution (* com. Javafirst. Service. * * (Java. Lang. string, Java. Lang. integer)): specifies all methods under the package and its sub packages, regardless of the method return type (you can write one more method in the interface for testing)
- execution(void com.javafirst.service.*.*(..)): Specify all methods under the package and its sub packages, regardless of whether there are parameters. (this can test whether another method defined in our previous interface will also execute the contents of the proxy method before execution)
We can use links &, |, |, |! To represent the relationship between "and", "or" and "not". However, when using XML file configuration, these symbols have special meanings, so we use "and", "or" and "not". There are no examples here. Let's see the notice notes below
[the external chain image transfer fails, and the source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-nsRZEoz7-1642126295233)(E:/AndroidStudioProject/MySourceProject/Java_md/SpringAOP%E6%B3%A8%E8%A7%A3.png)]
@Before
Pre notification: the notification method is called before the target method.
We have already learned how to use it. Here we can learn more about the parameters of the annotation. The Method name can be customized. Then a parameter of the system is JoinPoint. To use it, it must be the first parameter in the formal parameter list of the Method. Its function is similar to the Method class in Java reflection. It can obtain the Method name and Method parameters for different logical processing.
For example, we can verify the results by deforming the previous code:
@Before(value = "execution(void com.javafirst.service.*.*(..))") public void aop_before(JoinPoint point) { System.out.println(); System.out.println("Specify all methods under the package and its sub packages,It does not distinguish whether there are parameters or not-Execute the logic before the original business method. Here is the code to be executed by the automatic proxy function."); System.out.println("Method name:" + point.getSignature().getName()); System.out.println("Method parameters:" + point.getArgs().length + " Parameters"); if (point.getArgs().length == 2) { System.out.println("Method to execute two parameters"); } else { System.out.println("To execute a parameterless method"); } }
@AfterReturning
Post notification: execute after the target method is executed. You can also receive the return result of the target method from the parameters of this method. It is recommended to use Object type to receive.
1. Add a new method based on the original interface class:
String returnPrice(double price,float discount);
To test the post notification, we need to get the return value of the target method before executing the proxy method, so the method with return value and parameters is defined here.
2. Example of implementation logic in implementation class (actual development is based on business):
@Override public String returnPrice(double price, float discount) { if (discount > 0.0f && price > 0.0) { return "discount price:" + (price * discount); } return "original price:" + price; }
3. Define the post notification method in the facet class
/** * Post notification * * @param res The parameter name must be consistent with the return value, indicating the return value of the target method */ @AfterReturning(value = "execution(java.lang.String com.javafirst.service.impl.*.return*(..))", returning = "res") public void aop_afterReturning(Object res) { System.out.println(); System.out.println("Target method execution results:" + res); System.out.println("Output after target method execution."); }
The pointcut expression here is the same as before. The key point is the relationship between the return value here and the formal parameter name of the method. The two must be consistent, otherwise the return result of the target method will not be obtained.
4. The test code will not be posted. You can test it yourself
@Around
Encapsulating the target method is a simple understanding: the relevant code logic can be executed before and only after the target method. Here is an example:
1. Define interface methods
String doWork(String name);
2. Code in interface implementation class:
@Override public String doWork(String name) { System.out.println("Engineer in progress:" + name); return "Employee name:" + name; }
Note the execution timing of the return value here.
3. Annotation notification code defined in facet class:
/** * Around Advice * <p> * Definition of method: * - Must be public * - There must be a return value. Object is recommended * - Parameter must have and be ProceedingJoinPoint * * @param joinPoint * @return */ @Around(value = "execution(public java.lang.String com.javafirst.service.impl.SomeServiceImpl.doWork(java.lang.String))") public Object aop_around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println(); System.out.println("I am@Around Output in method"); // The parameter ProceedJoinPoint controls whether the target method can be executed Object args[] = joinPoint.getArgs(); if (null != args && args.length > 0) { // Execution target method if (null != args[0]) { joinPoint.proceed(); return "The parameters you passed in are:" + args[0]; } } return "I am@Around method return Content of"; }
If the target method is not executed through the parameter ProceedingJoinPoint, the default target method will not be executed; Note that the output statement and return value execution logic here to understand the surround notification.
4. Test code
/** * @Around The relevant code can be executed before and after the target method */ @Test public void testAround() { String config = "applicationContext.xml"; ApplicationContext context = new ClassPathXmlApplicationContext(config); SomeService someService = (SomeService) context.getBean("someService"); String result = someService.doWork("Li Chungang"); //String result = someService.doWork(null); System.out.println("Test result output:"+result); }
As a result, you can experience it personally. Observe the execution logic of our notification code by changing parameters. Surround notification is not equal to @ Before + @AfterReturning. Surround notification can modify the return value of the target method.
@AfterThrowing (understand)
The target method will execute after throwing an exception. If it does not throw an exception, it will not execute (that is * * if the target method tries to catch an exception, the notification method will not execute). Here is an example:
1. Define interface methods
void doOrder(Integer num);
In order to verify the results, we need to create exceptions through parameters. For convenience, simplicity and easy understanding, 200/num is used in the target method. If the parameter is 0, the exception occurs.
2. The implementation code of the implementation class, that is, the target class, is as follows:
@Override public void doOrder(Integer num) { System.out.println("Output within target method,If the parameter is 0, it is calculated(2022/num)Will throw an exception"); num = 2022 / num; }
3. Facet class, defining notification method code:
/** * Exception notification: after the target method throws an exception, it calls and does not throw exceptions. * <p> * Function: it plays the role of monitoring target methods. If there are exceptions, it is convenient for developers to locate problems and fix bug s * * @param ex */ @AfterThrowing(value = "execution(public void com.javafirst.service.impl.SomeServiceImpl.doOrder(java.lang.Integer))", throwing = "ex") public void aop_afterThrowing(Exception ex) { System.out.println("This sentence will be output only when the target method has an exception!\n Exception information:" + ex.getMessage()); }
The above is the core code. As a result, you can test it yourself.
@After (understand)
Final notification: the target method is called after returning or abnormal. The notification method will always be invoked to make some ending work, such as clear cache, delete some data, etc.
1. Define interface methods
void payMoney(String address);
As above, we "create exceptions" through the parameters of the method.
2. Code in implementation class:
@Override public void payMoney(String address) { System.out.println("Content of the first sentence of the target method, output parameters:" + address); System.out.println("Intercept the first three words of the address:" + (address.substring(0, 3))); //try { // System.out.println("the first three words of the intercepted address:" + (address.substring(0, 3))); //}catch (Exception ex){ // System.out.println("the target method has a try catch"); //} System.out.println("Output statement after target method exception."); }
3. Notification code defined in facet class:
/** * Final notification: must be executed to and after the target method */ @After(value = "execution(* *..SomeServiceImpl.payMoney(..))") public void aop_after() { System.out.println("\n Output content in facet class!"); }
As a result, you can verify it yourself. This is not difficult to understand. It is similar to the finally {} code block in try catch finally in Java. It will always be executed.
@Pointcut
-
What problem to solve: when we define notifications, we will add annotations on each method. There are Pointcut expressions in the annotations. When we define more methods and need to change the path or method name, it is cumbersome and prone to errors. Then the @ Pointcut annotation solves this problem.
-
Usage: define a method. There is no content in the method body. Add @ Pointcut annotation on the method. You should have thought of it. The annotation also has value attribute, so the expression is written here. If you want to change it in the future, just change this place; In the original annotation notification method, you only need to change the value expressed by the Pointcut to the method name defined here (in parentheses)
Let's demonstrate it. Take an annotation code we learned at the end of this article as an example:
@After(value = "aop_pointcut()") public void aop_after() { System.out.println("\naop_after() Post notification: output content in aspect class!"); } /** * Pre notification to test @ Pointcut */ @Before(value = "aop_pointcut()") public void aop_before_pointcut() { System.out.println("aop_before_pointcut() Pre notification: output content in aspect class!\n"); } /** * Define @ Pointcut annotation */ @Pointcut(value = "execution(* *..SomeServiceImpl.payMoney(..))") private void aop_pointcut() { }
The test code does not need to be modified. Just look at the results. In fact, it is not difficult. It has the same function as the include tag we used when learning dynamic SQL.
summary
After seeing this, we have finished learning about the two core contents of spring, and we will learn two main contents later: Spring integrates MyBatis and spring transactions.
- Spring aspect oriented programming is a programming idea. In fact, it is not as difficult to understand as expected, especially when you have the foundation of java programming.
- Remember the five notification annotations demonstrated in this article, their usage, code execution process, and what business logic we should deal with at that stage, which will be used later.
tips: the source code of this article can be viewed in reply to aop in official account recommendation java.
Learn programming and recommend Java language. There is no doubt. If you still don't master the Java System enough, you can contact Xiaobian and advance with Xiaobian!
After reading, remember to point praise and comments to support Xiaobian ha. The codeword is not easy~