头图

Preface

This article is the fifth article of "How to implement a simple version of Spring series". Before I introduced one of the core technologies in Spring IoC. From this article, let’s take a look at another important Spring technology- AOP . Friends who have used the Spring framework for development believe that they should have been in contact with AOP more or less. The Chinese description is programming aspect It is very important to learn a new technology and understand its background. When you first started to contact AOP, I wonder if you have thought about this problem. Since there is already OOP in object-oriented languages, why do you need AOP? What? To put it another way, that is to say, which scenes in OOP are actually not handled elegantly, and need to find a new technology to solve the problem? (PS here recommends to pause for ten seconds, think about it for yourself...)

Why do we need AOP

The ultimate goal of our software development is to solve the company’s various requirements and empower the business. Note that the requirements here include business requirements and system requirements. For most of the common concerns of business requirements, you can It is well abstracted, encapsulated, and modularized through object-oriented (OOP) methods. However, although the object-oriented approach to system requirements is well decomposed and modularized, it is not very good. Avoid these similar system requirements scattered in the various modules of the system problem.

why-need-aop.png

Therefore, it is necessary to find a better way to provide a new set of methods based on OOP to deal with the above problems, or to add a supplement to the OOP object-oriented development model, so that it can be more To deal with the above problems gracefully, one of the solutions Spring has provided so far is aspect-oriented programming-AOP. With AOP, we can organize these system requirements (cross-cutting concerns: cross-cutting concern) such as transaction management, system logs, and security checks into a modular organization, making the entire system more modular to facilitate subsequent management and maintenance . Careful, you should find that a key abstraction is introduced in AOP, the aspect, which is used to encapsulate some cross-cutting concerns in the system. One thing to be clear is that AOP and OOP are not an all-or-nothing relationship. , AOP is a supplement and improvement to OOP, which can cooperate with each other to complete requirements. Aspect is as important to AOP as Class is to OOP.

use-aop-arc.png

Several important concepts

Our ultimate goal is to imitate the Spring framework to implement a simple version of AOP. Although it is a simple version, it will involve the core ideas and main implementation steps in Spring AOP, but before that, let’s take a look at the important aspects of AOP. The concept is also to lay the theoretical foundation for future implementation. It should be noted here that I will not use Chinese translation to describe these AOP-defined terms (in addition, the industry AOP terminology is inherently not uniform), and you need to focus on the terminology. The meaning represented in AOP is like we don't translate Spring into Spring. In software development communication, you know that it means a Java development framework. The key terms are introduced one by one below:

Joinpoint

A point during the execution of a program, such as the execution of a method or the handling of an exception. In Spring AOP, a join point always represents a method execution. -- Spring Docs

From the previous introduction, we know that before our system runs, we need to weave (which can be simply understood as embedded) into some business modules of the system, and we want to complete The premise of weaving is that we need to know which execution points can operate on, and these execution points are Joinpoints. Let's look at a simple example:

/**
 * @author mghio
 * @since 2021-05-22
 */
public class Developer {

  private String name;

  private Integer age;

  private String siteUrl;

  private String position;

  public Developer(String name, String siteUrl) {
    this.name = name;
    this.siteUrl = siteUrl;
  }

  public void setSiteUrl(String siteUrl) {
    this.siteUrl = siteUrl;
  }

  public void setAge(Integer age) {
    this.age = age;
  }

  public void setName(String name) {
    this.name = name;
  }

  public void setPosition(String position) {
    this.position = position;
  }

  public void showMainIntro() {
    System.out.printf("name:[%s], siteUrl:[%s]\n", this.name, this.siteUrl);
  }

  public void showAllIntro() {
    System.out.printf("name:[%s], age:[%s], siteUrl:[%s], position:[%s]\n",
        this.name, this.age, this.siteUrl, this.position);
  }

}
/**
 * @author mghio
 * @since 2021-05-22
 */
public class DeveloperTest {

  @Test
  public void test() {
    Developer developer = new Developer("mghio", "https://www.mghio.cn");
    developer.showMainIntro();
    developer.setAge(18);
    developer.setPosition("中国·上海");
    developer.showAllIntro();
  }

}

Theoretically, in the test() method call of the above example, we can choose to weave during the execution of the Developer’s construction method, or weave at the execution point of the showMainIntro() method (where it is called or in The internal execution of the method), or weaving when the sge field is set in the setAge() method. In fact, as long as you want to perform the weaving at any execution point of the test() method, the execution points that can be woven are Joinpoint.
This may be more abstract. Let's take a look at it intuitively through the sequence diagram called by the test() method:

aop-weaving.png

From the timing of method execution, it is not difficult to find that there are some common Joinpoint types as follows:

  • Constructor Call . The execution point for initializing an object by calling its constructor, such as Developer developer = new Developer("mghio", "https://www.mghio.cn"); in the above code.
  • Method call . The execution point when the method of an object is called. In fact, the construction method is also a special case of method invocation, but the method here is the construction method, such as developer.showMainIntro(); and developer.showAllIntro( in the example ); All of this type.
  • Method execution . When a method is called, the execution point of the program inside the method is the execution point inside the called method. Unlike method invocation, the method execution is shown in the above method sequence diagram.
  • Field set . Call the object's setter method to set the code execution point of the object field. The trigger point is that the object's properties are set, regardless of the setting method. The developer.setAge(18); and developer.setPosition("China.Shanghai"); in the above example are both of this type.
  • Class initialization . The initialization execution points of some static fields or static code blocks in the class are not reflected in the above examples.
  • Exception execution . The execution point of the corresponding exception handling logic after some method of the class throws an exception. There is no such type in the above example.

Although theoretically, any execution point in the program execution can be used as a Joinpoint, but weaving operations on certain types of execution points are more expensive, so Joinpoint in Spring only supports method execution (Method execution ) This type (this is also explained from Spring's official documentation), in fact, this type can meet most of the scenarios.

Pointcut

A predicate that matches join points. Advice is associated with a pointcut expression and runs at any join point matched by the pointcut (for example, the execution of a method with a certain name). The concept of join points as matched by pointcut expressions is central to AOP, and Spring uses the AspectJ pointcut expression language by default.-- by Spring Docs

Pointcut represents a type of Jointpoint expression. When weaving, we need to configure the Pointcut according to the configuration of the Pointcut, and then weave the logic of crosscutting to those matching Joinpoints. The first problem we face here: using human natural language can quickly express which Joinpoints we need to weave, but how to express these Joinpoints in the code?
At present, there are some ways of expressing the definition of Joinpoint:

  • directly specifies the weaving method name . Obviously, although this way of expression is simple, the supported functions are relatively single, and only apply to the method type Joinpoint, and when there are more methods to be woven in our system, it is too troublesome to define the woven Pointjoint one by one. .
  • regular expression mode . I believe everyone has some understanding of regular expressions, and they are very powerful. They can match Jointpoints that represent multiple different method types. The AOP of the Spring framework also supports this way of expression.
  • Pointcut Specific language method . Because this is a domain-specific language (DSL), it also provides the most flexible and rich functions, which also leads to a relatively high level of complexity in both its use and implementation. AspectJ uses this expression method, of course. Spring also supports it.

In addition, Pointcut also supports some simple logical operations. At this time, we can combine multiple simple Pointcuts into a more complex Pointcut through logical operations, such as logical operation identifiers such as and and or in the Spring configuration and AspectJ Logical operators such as && and || in.

Advice

Action taken by an aspect at a particular join point. Different types of advice include “around”, “before” and “after” advice. (Advice types are discussed later.) Many AOP frameworks, including Spring, model an advice as an interceptor and maintain a chain of interceptors around the join point.-- by Spring Docs

Advice represents a crosscutting logic injected into Joinpoint, which is an abstract carrier of crosscutting concern logic. According to the location and function of the execution point of Advice, it is divided into the following main types:

  • Before Advice . Before Advice represents the type that is executed before the matched Joinpoint position. If it is successfully woven into the Joinpoint of the method type, then Beofre Advice will be executed before this method is executed. One more thing to note is that if you need to end the execution of the method in Before Advice, we can pass in Advice by throwing End the execution of the method in an abnormal way.
  • After Advice . Obviously, After Advice represents the type of execution after the configured Joinpoint location. It can be subdivided into three types: After returning Advice, After throwing Advice and After finally Advice. After returning Advice means that the matching Joinpoint method is executed after normal execution (no exception is thrown); After throwing Advice means that an exception is thrown during the execution of the matching Joinpoint method and it is executed after it has not returned normally; After finally Advice means the method type Joinpoint will be executed regardless of whether it is executed normally or throws an exception.

The execution sequence of these Advice types in the Joinpoint of the method type is shown in the following figure:
advice-example-location.png

  • Around Advice . This type is the most powerful advice. It can match the execution process of Joinpoint before, after, or even the terminal. Under normal circumstances, the execution logic before Joinpoint will be executed first, then Joinpoint's own execution flow, and finally Joinpoint will be executed. The execution logic afterwards. You should be careful. Isn't this a combination of the Before Advice and After Advice types described above? Yes, it can perform these two types of functions, but you still have to choose the appropriate Advice type according to the specific scenario.

Aspect

A modularization of a concern that cuts across multiple classes. Transaction management is a good example of a crosscutting concern in enterprise Java applications. In Spring AOP, aspects are implemented by using regular classes (the schema-based approach) or regular classes annotated with the @Aspect annotation (the @AspectJ style). -- Spring Docs

Aspect is an abstract concept after packaging the crosscutting concern in our system. It can contain multiple Joinpoint and multiple Advice definitions. After Spring integrates AspectJ, you can also use @AspectJ-style declarative designation of an Aspect, as long as you add the @Aspect annotation.

Target object

An object being advised by one or more aspects. Also referred to as the “advised object”. Since Spring AOP is implemented by using runtime proxies, this object is always a proxied object. -- by Spring Docs

The target object generally refers to those objects that can match the Pointcut statement conditions and are woven into the crosscutting logic. Normally, they are determined by Pointcut and will vary according to the pointcut setting conditions.
Once you have these concepts of AOP, you can organize the above examples again. The location of each concept is shown in the following figure:

aop-concept.png

to sum up

This article first gives a brief introduction to the background of the birth of AOP technology, and then introduces several important concepts of AOP to lay the foundation for our own implementation of a simple version of AOP. AOP is a supplement and improvement to OOP. Several are listed in the article. The concept is just the tip of the iceberg of the concepts involved in AOP. Friends who want to learn more about the related concepts can read the official document learn. The next part is to introduce some basic technologies that AOP implementation depends on, so stay tuned. Reposting and sharing are all support for me, and I will be more motivated to insist on original sharing!


mghio
446 声望870 粉丝