preface

In the previous source code parsing chapter, I explained the source code of the core part of Spring IOC. If you are familiar with the use of Spring AOP, after understanding the core source code of Spring IOC, it should be easy to learn the source code of Spring AOP.

However, I feel a little awkward to start talking about the Spring AOP source code directly, so I have this chapter. Introduction to Spring AOP: includes some conceptual introduction and configuration methods of Spring AOP.

Here’s a mind map.

What is the AOP

AOP: Aspect Oriented Programming

An Aspect is a new modular mechanism for describing crosscutting concerns scattered across objects, classes, or functions. The separation of crosscutting concerns from concerns is a central concept of section-oriented programming. Separation of concerns to solve the problem of specific area comes out independently in code from business logic, business logic code no longer contains code for domain specific problem call, business logic and the problems in specific areas by plane to encapsulate, maintenance, so that scattered throughout the changes in the applications can manage well.

Recently, in the book “Technology Architecture of Large Websites” by Li Zhihui, the author mentioned that developing low coupling system is one of the ultimate goals of software design. AOP’s approach to aspect oriented programming embodies this idea. Some repetitive functional code (logging, security management, etc.) that is not related to the main business logic is packaged by modularized dissection of sections to achieve separation of concerns and module decoupling, making the entire system easier to maintain and manage.

This divide-and-conquer design makes me feel a sense of beauty.

What AOP wants to achieve is to carry out certain packaging on the basis of our original code, such as certain interception processing or enhancement processing before method execution, after method return, after method throws an exception, and so on.

AOP is implemented not because Java provides some magic hook that tells us the lifecycle of a method, but because we want to implement a proxy, and the actual running instance is actually an instance of the generated proxy class.

Noun concept

As mentioned earlier, Spring AOP extends concepts from AspectJ, using annotations in the JAR packages provided by AspectJ. The concepts and terms within Spring AOP are not unique to Spring but are AOP related.

Concepts can be skimped over, and come back later to understand them better.

The term concept
Aspect Cross section isPointcutandAdvice, usually as a separate class.PointcutandAdviceIt defines everything about the aspect. It isWhen, when and whereComplete the function.
Joinpoint This represents a point where you can insert an AOP aspect into your application. In other words, this is the actual place in your application to take action using the Spring AOP framework.
Advice This is the actual action taken before or after the method is executed. This is the actual code snippet that is invoked during program execution of the Spring AOP framework.
Pointcut This is a set of one or more pointcuts that should be executed at the pointcutAdvice. You can specify pointcuts using expressions or patterns, as described in the following examples.
Introduction References allow us to add new methods or properties to existing classes
Weaving The process of creating an enhanced object. This can be done at compile time (using the AspectJ compiler, for example) or at run time. Spring, like other pure Java AOP frameworks, is woven at run time.

Why do so many Chinese articles on the Internet translate advice into “notice”? Does it make sense conceptually?? I prefer to translate it as “enhanced”.

There are also annotations indicating the type of Advice, or the timing of the enhancement, which will become clearer after looking at the following examples.

The term concept
Before The enhancement is performed before the method is called
After The enhancement is performed after the method has been called
After-returning The enhancement is performed after the method has successfully executed
After-throwing The enhancement is performed after the method throws the specified exception
Around Perform custom enhanced behavior before and after method calls (the most flexible way)

use

Since Spring 2.0, Spring AOP has been configured in two ways.

  1. Schema-based: After Spring 2.0, it is configured using XML, using namespace < AOP />

  2. AspectJ configuration: Annotations provided after Spring 2.0. It’s called @aspectJ, but it has nothing to do with AspectJ.

PS: I prefer the @aspectJ way, which is the most aspect to use. It may also be because I find Spring beans configured in XML to be very unconcise and writable, so I am a little reluctant. 23333 –

This article mainly explains the annotation method, and gives the corresponding DEMO; Later source code parsing will also use annotations as an example to explain Spring AOP source code (the whole source code parsing will be similar to other methods, because the principles are the same).

Those of you who are interested in other configurations can Google other learning materials.


Here’s a dividing line. Here we go

1. Open@AspectJNote Configuration Mode

There are two ways to enable @AspectJ annotation configuration

  1. Configure in XML:

    <aop:aspectj-autoproxy/>
    Copy the code
  2. Use the @enableAspectJAutoProxy annotation

    @Configuration
    @EnableAspectJAutoProxy
    public class Config {}Copy the code

With this configuration enabled, all beans in the container that are annotated by @AspectJ are treated by Spring as AOP configuration classes, called aspects.

NOTE: One thing to NOTE here is that @AspectJ annotations can only be applied to Spring beans, so classes you decorate with @Aspect will either be decorated with @Component annotations or configured in XML.

For example,

// A valid AOP configuration class
@Aspect
@Component
public class MyAspect {
 	//....   
}

// This is an invalid AOP configuration class if it has not been configured in XML
@Aspect
public class MyAspect {
 	//....   
}
Copy the code

2. Configure the Pointcut (enhanced Pointcut)

Pointcuts are mostly translated as pointcuts to define which methods need to be enhanced or intercepted.

In Spring, we can think of a Pointcut as a method to match all beans in the Spring container that meet a specified condition.

For example,

    // The specified method
    @Pointcut("execution(* testExecution(..) )")
    public void anyTestMethod(a) {}
Copy the code

Here’s a complete list of how pointcuts match:

  1. Execution: Matches method signatures

    The simplest way to do this is in the example above, “execution(* testExecution(..) “)” means to match a method named testExecution, * means any return value, (..) Represents zero or more arbitrary parameters.

  2. **within: ** specifies the method under the class or package (Spring AOP only)

        / / service layer
        / / ".." Represents a package and its subpackages
        @Pointcut("within(ric.study.demo.aop.svc.. *)")
        public void inSvcLayer(a) {}
    Copy the code
  3. @annotation: There is a specific annotation on a method

        // Specify annotations
        @Pointcut("@annotation(ric.study.demo.aop.HaveAop)")
        public void withAnnotation(a) {}
    Copy the code
  4. Beans (IDORNAMeneof Bean) : Match Bean name (Spring AOP exclusive)

        / / the controller layer
        @Pointcut("bean(testController)")
        public void inControllerLayer(a) {}
    Copy the code

These are several common configurations in daily use

For more detailed matching requirements, see this article: www.baeldung.com/spring-aop-…

Spring has this advice for configuring pointcuts:

When working with enterprise applications, you often want to refer to modules of the application and particular sets of operations from within several aspects. We recommend defining a “SystemArchitecture” aspect that captures common pointcut expressions for this purpose. A typical such aspect would look as follows:

That being said, if you are developing an enterprise application, Spring recommends that you use the SystemArchitecture aspect configuration, in which the common PointCut configuration is maintained in a single class. The official website documentation gives an example like this (it is configured using XML, so there is no @Component annotation).

package com.xyz.someapp;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class SystemArchitecture {

  /** * A join point is in the web layer if the method is defined * in a type in the com.xyz.someapp.web package or any sub-package * under that. */
  @Pointcut("within(com.xyz.someapp.web.. *)")
  public void inWebLayer(a) {}

  /** * A join point is in the service layer if the method is defined * in a type in the com.xyz.someapp.service package or any sub-package * under that. */
  @Pointcut("within(com.xyz.someapp.service.. *)")
  public void inServiceLayer(a) {}

  /** * A join point is in the data access layer if the method is defined * in a type in the com.xyz.someapp.dao package or any sub-package * under that. */
  @Pointcut("within(com.xyz.someapp.dao.. *)")
  public void inDataAccessLayer(a) {}

  /** * A business service is the execution of any method defined on a service * interface. This definition assumes that interfaces are placed in the * "service" package, and that implementation types are in sub-packages. * * If you group service interfaces by functional area (for example, * in packages com.xyz.someapp.abc.service and com.xyz.def.service) then * the pointcut expression "execution(* com.xyz.someapp.. service.*.*(..) )" * could be used instead. */
  @Pointcut("execution(* com.xyz.someapp.service.*.*(..) )")
  public void businessService(a) {}
  
  /** * A data access operation is the execution of any method defined on a * dao interface. This definition assumes that interfaces are placed in the * "dao" package, and that implementation types are in sub-packages. */
  @Pointcut("execution(* com.xyz.someapp.dao.*.*(..) )")
  public void dataAccessOperation(a) {}}Copy the code

The SystemArchitecture above is easy to understand. This Aspect defines a bunch of pointcuts that can then be referenced directly wherever a Pointcut is needed.

The configuration pointcut represents what methods we want the program to intercept, but how the program needs to enhance the methods of interception is described in the configuration Advice section below.

3. The configuration Advice

Note that in practice, Aspect classes should adhere to the single responsibility principle. Do not write all Advice configurations in one Aspect class.

I wrote them together just for the sake of illustration.

Let’s go straight to the example code, which contains several ways to configure Advice (as mentioned in the noun concepts section above).

/** * Note: In actual development, Advice should follow a single responsibility and should not be mixed **@author Richard_yyf
 * @version1.0 2019/10/28 * /
@Aspect
@Component
public class GlobalAopAdvice {

    @Before("ric.study.demo.aop.SystemArchitecture.dataAccessOperation()")
    public void doAccessCheck(a) {
        / /... The implementation code
    }

    // In practice, you can combine Advice and Pointcut like this, and define the Pointcut directly on the Advice
    @Before("execution(* ric.study.demo.dao.*.*(..) )")
    public void doAccessCheck(a) {
        / /... The implementation code
    }

    / / in the method
    @AfterReturning("ric.study.demo.aop.SystemArchitecture.dataAccessOperation()")
    public void doAccessCheck(a) {
        / /... The implementation code
    }

    // returnVal is the return value of the corresponding method
    @AfterReturning(
        pointcut="ric.study.demo.aop.SystemArchitecture.dataAccessOperation()",
        returning="returnVal")
    public void doAccessCheck(Object returnVal) {
        / /... The implementation code
    }

    // When an exception returns
    @AfterThrowing("ric.study.demo.aop.SystemArchitecture.dataAccessOperation()")
    public void doRecoveryActions(a) {
        / /... The implementation code
    }

    // Be aware of the difference between this and @AfterRETURNING, which intercepts normal returns and exceptions
    @After("ric.study.demo.aop.SystemArchitecture.dataAccessOperation()")
    public void doReleaseLock(a) {
        // This is usually used as a finally block to free resources.
        // Both normal return and abnormal exit will be intercepted
    }

    // It's very flexible. It can work with @before and @afterRETURNING
    @Around("ric.study.demo.aop.SystemArchitecture.businessService()")
    public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
       	// Before the target method executes... The implementation code
        Object retVal = pjp.proceed();
        // After the target method executes... The implementation code
        returnretVal; }}Copy the code

In some scenarios, we want to @ Before, to obtain the method into the parameter, such as for some log records, we can through the org. Aspectj. Lang. JoinPoint. The ProceedingJoinPoint above is a subclass of it.

@Before("...")
public void logArgs(JoinPoint joinPoint) {
    System.out.println("Before the method executes, print in the arguments:" + Arrays.toString(joinPoint.getArgs()));
}
Copy the code

Here’s a corresponding, method return print:

@AfterReturning( pointcut="...", returning="returnVal")
public void logReturnVal(Object returnVal) {
    System.out.println("After the method executes, print the backarguments:" + returnVal));
}
Copy the code

A quick Demo

After introducing the above configuration process, let’s use a quick Demo to demonstrate it in practice. I’ll change the order here;

1. Write the target class

package ric.study.demo.aop.svc;

public interface TestSvc {

    void process(a);
}

@Service("testSvc")
public class TestSvcImpl implements TestSvc {
    @Override
    public void process(a) {
        System.out.println("test svc is working"); }}public interface DateSvc {

    void printDate(Date date);
}

@Service("dateSvc")
public class DateSvcImpl implements DateSvc {

    @Override
    public void printDate(Date date) {
        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date)); }}Copy the code

2. Configuration Pointcut

@Aspect
@Component
public class PointCutConfig {
    @Pointcut("within(ric.study.demo.aop.svc.. *)")
    public void inSvcLayer(a) {}}Copy the code

3. The configuration Advice

/ * * *@author Richard_yyf
 * @version1.0 2019/10/29 * /
@Component
@Aspect
public class ServiceLogAspect {

    // Intercept, print logs, and use JoinPoint to obtain method parameters
    @Before("ric.study.demo.aop.PointCutConfig.inSvcLayer()")
    public void logBeforeSvc(JoinPoint joinPoint) {
        System.out.println("Print the first log before the service method executes");
        System.out.println("The method signature of the intercepted service method:" + joinPoint.getSignature());
        System.out.println("Intercepting service method method input:" + Arrays.toString(joinPoint.getArgs()));
    }

    // Here is how the Advice and Pointcut are configured together
    @Before("within(ric.study.demo.aop.svc.. *)")
    public void logBeforeSvc2(a) {
        System.out.println("Print a second log before the service method executes."); }}Copy the code

4. Open@AspectJNote configuration mode, and start

The configuration class and the startup class are written together for convenience,

/ * * *@author Richard_yyf
 * @version1.0 2019/10/28 * /
@Configuration
@EnableAspectJAutoProxy
@ComponentScan("ric.study.demo.aop")
public class Boostrap {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Boostrap.class);
        TestSvc svc = (TestSvc) context.getBean("testSvc");
        svc.process();
        System.out.println("= = = = = = = = = = = = = = = = = =");
        DateSvc dateSvc = (DateSvc) context.getBean("dateSvc");
        dateSvc.printDate(newDate()); }}Copy the code

5. The output

Before the service method perform print first log intercept service signature of the method: void ric. Study. Demo. Aop. SVC. TestSvcImpl. Process intercept () method of the service method into the reference: Test SVC is working ================== Print the method signature of the service method intercepted in the first log before the service method is executed: Void ric. Study. Demo. Aop. SVC. DateSvcImpl. PrintDate (Date) to intercept the service method, method into the arguments: [Mon Nov 04 18:11:34 CST 2019] Print the second log before the service method is executedCopy the code

JDK dynamic proxy and Cglib

As mentioned earlier, Spring AOP uses THE JDK dynamic proxy to generate proxy classes when the target class has an implementation interface. Let’s take a look at the DEMO above.

What if we want to enforce the Cglib approach regardless of whether there is an implementation interface?

Spring provides us with a proxy-target-class configuration.

Annotation method:// @enableAspectJAutoProxy (proxyTargetClass = true<aop:config proxy-target-class="true">
Copy the code

When I changed it,

summary

This article detailed the origins of Spring AOP, its noun concepts, and its annotation-based usage.

This article in accordance with the author’s writing habits, is the source code analysis chapter of the pre-learning chapter. In the next chapter, we will introduce the source code design of Spring AOP, using annotations as an entry point, and interpret the relevant core source code (the whole source code parsing will make sense of the other ways, because the principles are the same).

If you are interested, you can turn to the introduction section and look at mind mapping.

If this article helps you, I hope you can give it a like, it is the biggest motivation for me.