Before writing Spring IOC source analysis, many readers hope to be able to give a Spring AOP source analysis, but Spring AOP source code or more, write out the length will be large.

Rather than source code analysis, this article introduces some of the concepts in Spring AOP and its various configuration methods, covering all three of the configuration methods that Spring AOP has evolved to date.

Because of Spring’s powerful backward compatibility, there are often a lot of messy configuration situations in the actual code that actually work, and this article is intended to help clarify that.

The test source code used in this article has been uploaded to Github: Hongjiev/Spring-AOP-Learning.

AOP, AspectJ, Spring AOP

Let’s first explain their concepts and relationships.

AOP to achieve is on the basis of our original written code, for certain packaging, such as in the method before execution, method return, method after throwing exceptions and other places for certain interception or enhancement processing.

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

As Java developers, we are all familiar with the term AspectJ, and when we even think of AOP, AspectJ is what comes to mind, even if you don’t quite understand how it works. Here’s a simple comparison between AspectJ and Spring AOP:

Spring AOP:

  • It is implemented based on dynamic proxy. By default, the dynamic proxy implementation provided by the JDK is used if interfaces are used, and CGLIB is used if no interfaces are available. Make sure you understand the implications, including when you will use CGLIB instead of dynamic proxies provided by the JDK.

  • Since Spring 3.2, spring-Core includes the source code for CGLIB and ASM directly, which is why we don’t need to explicitly introduce these dependencies

  • Both Spring’s IOC container and AOP are important, and Spring AOP needs to rely on the IOC container for management.
  • If you’re a Web developer, at some point, you’ll probably need a Filter or an Interceptor, not necessarily AOP.
  • Spring AOP only works on beans in the Spring container. It is implemented using pure Java code and only works on Bean methods.
  • Spring provides AspectJ support, which we’ll cover separately later, but pure Spring AOP is generally enough.
  • Many people compare the performance of Spring AOP with that of AspectJ. Spring AOP is based on proxy implementation, generating proxy instances at container startup and adding stack depth to method calls, making Spring AOP not as good as AspectJ’s performance.

AspectJ:

  • AspectJ is also from the Eclipse Foundation, link: www.eclipse.org/aspectj
  • It is static weaving, which is implemented by modifying the code, and its weaving time can be:
    • Compile time weaving: if class A uses AspectJ to add an attribute and class B references it, the scenario needs to weave at Compile time otherwise class B will not Compile.
    • Post-compile: the weaving method is where the.class file is generated, or the weaving method is used to improve the weaving.
    • Load-time weaving: refers to weaving while the class is being loaded. There are several common ways to do this. 1. Custom class loaders do this, which is probably the easiest way to load classes before they are woven into the JVM, so you can define the behavior at load time. Specify aspectJ-provided agent at JVM startup time:-javaagent:xxx/xxx/aspectjweaver.jar.
  • AspectJ can do a lot of things Spring AOP can’t, and it is a complete solution to AOP programming. Rather than being a complete solution to AOP programming like AspectJ, Spring AOP addresses the most common AOP requirement in enterprise development (method weaving).

  • Because AspectJ does the weaving before the actual code runs, one could argue that it generates classes with no additional runtime overhead.

  • I will soon write an article on AspectJ and how to use AspectJ in Spring applications.

AOP Terminology

Instead of trying to explain as many AOP programming terms as we can here, let’s go with each one we encounter.

Advice, Advisor, Pointcut, Aspect, Joinpoint, and more.

Spring AOP

First of all, Spring AOP is pure Spring code and has nothing to do with AspectJ, but Spring uses concepts from AspectJ, including annotations from jar packages provided by AspectJ. But it does not depend on its implementation.

Annotations such as @aspect, @pointcut, @before, and @After are all derived from AspectJ, but the functionality is implemented by pure Spring AOP itself.

Here we will introduce the use of Spring AOP, starting with the simplest configuration, so that readers want to see the source will be easier.

There are currently three ways to configure Spring AOP, and Spring provides good downward compatibility, so you can use it with confidence.

  • Spring 1.2 Interface-based configuration: The earliest Spring AOP was based entirely on a few interfaces, so those who want to look at the source code can start here.
  • Spring 2.0 Schema – -based configurationSpring 2.0 uses XML for configuration, using namespaces<aop />
  • Spring 2.0 @aspectj configuration: Use annotations for configuration, which feels the most convenient, and although this is called@AspectJBut this has nothing to do with AspectJ.

Configuration in Spring 1.2

This section we will introduce the configuration of Spring 1.2, it is the oldest of the configuration, but the Spring provides good backwards compatibility, and a lot of people don’t know what what the version of the configuration and whether have updated better alternative configuration method, so there will be a lot of code is to use the old configuration mode, I don’t mean old in a derogatory sense.

Here is a simple example to demonstrate how to use Spring 1.2 configuration.

First, we define two interfaces, UserService and OrderService, and their implementation classes, UserServiceImpl and OrderServiceImpl:

Next, we define two advice for intercepting methods before they execute and after they return:

Advice is the first concept we encounter. Remember what it is for.

The two Advice above are used to output parameters before and results after method calls, respectively.

We are now ready to configure. We will configure a file named spring_1_2.xml:

Next, let’s run and see:

View the output:

Prepare to execute method: createUser, parameter list: [Tom, Cruise, 55] Method return: User{firstName='Tom', lastName='Cruise', age=55, address='null'} Ready to execute method: queryUser, parameter list: [] method returns: User{firstName='Tom', lastName='Cruise', age=55, address='null'}
Copy the code

As you can see from the results, both methods in UserService are intercepted before and after. This example should be simple enough to understand as a proxy implementation.

The proxy pattern requires an interface, a concrete implementation class, and then defining a proxy class that wraps the implementation class, adds custom logic, and generates instances when used.

There is a fatal problem with this method. If we need to intercept methods in OrderService, then we also need to define a proxy for OrderService. If you want to intercept PostService, define a PostService proxy……

Furthermore, we see that the granularity of our interceptor is controlled only down to the class level, where all methods are intercepted. Next, let’s look at how to intercept only specific methods.

In the above configuration, when configuring interceptors, you can specify interceptorNames as Advice, as well as Interceptor and Advisor.

It needs to specify an Advice internally. The Advisor decides which methods to intercept and the internal Advice does the work.

It has several implementation class, here we use the implementation class NameMatchMethodPointcutAdvisor to demonstrate that you can see from the name, it needs us to provide the method name to it, so that conform to intercept this configuration method will do.

As you can see, the userServiceProxy bean is configured with an Advisor with an advice inside the Advisor. The advisor is responsible for matching the method, and the internal advice is responsible for implementing the method wrapper.

Note that the mappedNames configuration here can specify multiple, comma-separated, methods in different classes. Advisor provides more fine-grained control than directly specifying advice, because all methods are intercepted if advice is configured here.

Only the createUser method is blocked:

Preparation Method: createUser, parameter list: [Tom, Cruise, 55]Copy the code

Now that you know about Advice and Advisor, you can also configure Interceptor.

Interceptor: Interceptor: Interceptor: Interceptor: Interceptor: Interceptor: Interceptor: Interceptor: Interceptor

public class DebugInterceptor implements MethodInterceptor {

    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("Before: invocation=[" + invocation + "]"); Invocation method Object rval = Invocation. Proceed (); System.out.println("Invocation returned");
        returnrval; }}Copy the code

Advice, Advisor, and Interceptor are easy to understand.

The common problem is that we need to configure a proxy for each bean, and then we need to fetch the bean instance of the proxy class (e.g. (UserService) context.getBean(“userServiceProxy”)). This is obviously very inconvenient and not conducive to the automatic type-by-type injection that we will use later. The following describes the autoProxy solution.

Autoproxy: As the name suggests, it implements automatic proxy, which means that when Spring discovers that a bean needs to be textured, it automatically generates a proxy for that bean to intercept method execution and ensure that the defined aspect can be executed.

The emphasis here is on automation, which means that Spring does this automatically, without explicitly specifying the bean of the proxy class, as described earlier.

We removed the ProxyFactoryBean configuration and used BeanNameAutoProxyCreator instead:

The configuration is simple, and beanNames uses the re to match the name of the bean. After this configuration, the interceptors userServiceBeforeAdvice and userServiceAfterAdvice can not only apply to UserServiceImpl, Also available to OrderServiceImpl, PostServiceImpl, ArticleServiceImpl…… Wait, that is, no longer the agent that configures a bean.

Note that InterceptorNames can also be configured as Advisor and Interceptor.

Then we modify the places used:

We don’t need to worry about the proxy when we use it, just use the original type, which is very convenient.

The output is that each method in OrderService and UserService is intercepted:

Prepare to execute method: createUser, parameter list: [Tom, Cruise, 55] Method return: User{firstName='Tom', lastName='Cruise', age=55, address='null'} Ready to execute method: queryUser, parameter list: [] method returns: User{firstName='Tom', lastName='Cruise', age=55, address='null'} prepare to execute method: createOrder, parameter list: [Leo, buy anything] method return: Order{username='Leo', product='Anything you want'} Prepare to execute method: queryOrder, parameter list: [Leo] method return: Order{username='Leo', product='Anything you want'}
Copy the code

BeanNameAutoProxyCreator is a great way to specify a schema for the class name to be intercepted (e.g. *ServiceImpl). It can be configured multiple times so that it can be used to match classes with different schemas.

In addition, the BeanNameAutoProxyCreator the same package, there is a very useful class DefaultAdvisorAutoProxyCreator, more convenient than the BeanNameAutoProxyCreator above.

As we said earlier, advice is wrapped inside the Advisor, which is responsible for deciding which methods to intercept, and the internal advice defines the logic behind the intercept. Therefore, if you think about it carefully, you can implement the custom interception function and the logical processing after interception just by making our Advisor globally effective.

BeanNameAutoProxyCreator is a self-matching method that is then intercepted by internal advice.

And DefaultAdvisorAutoProxyCreator is let all the advisor in the ioc container matching method, internal advisor has advice, let them internal advice to intercept processing.

1, we need to look back the Advisor configuration, we use the above NameMatchMethodPointcutAdvisor this class:

<bean id="logCreateAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
    <property name="advice" ref="logArgsAdvice" />
    <property name="mappedNames" value="createUser,createOrder" />
</bean>
Copy the code

Actually Advisor and a more flexible implementation class RegexpMethodPointcutAdvisor, it can realize the regular match, such as:

<bean id="logArgsAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
    <property name="advice" ref="logArgsAdvice" />
    <property name="pattern" value="com.javadoop.*.service.*.create.*" />
</bean>
Copy the code

That is, we can configure the Advisor to pinpoint the method that needs to be intercepted, and then use the internal Advice to perform the logical processing.

2, after, we need to configure the DefaultAdvisorAutoProxyCreator, its configuration is simple, direct use of this configuration is ok, it will make all the Advisor on automatically, no other configuration.

<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />
Copy the code

Then let’s run it:

Output:

Prepare to execute method: createUser, parameter list: [Tom, Cruise, 55] Method return: User{firstName='Tom', lastName='Cruise', age=55, address='null'} prepare to execute method: createOrder, parameter list: [Leo, buy anything] method return: Order{username='Leo', product='Anything you want'}
Copy the code

You can see from the result that create

Method uses logArgsAdvisor for parameter output, query

This concludes the Spring 1.2 configuration. This article will not cover everything. It will focus on the core configuration. If you are interested, you should learn to explore it yourself. Such as the Advisor is not only I here introduce NameMatchMethodPointcutAdvisor and RegexpMethodPointcutAdvisor, Also not just BeanNameAutoProxyCreator and DefaultAdvisorAutoProxyCreator AutoProxyCreator.

At this point, I think many people will know how to read the Spring AOP source code.

Spring 2.0@AspectJ configuration

Since Spring 2.0, @AspectJ and Schema-based configurations have been introduced. Let’s first introduce @AspectJ configuration and then look at XML configuration.

Note that @AspectJ has little to do with AspectJ. It is not implemented based on AspectJ, but simply uses concepts from AspectJ, including using annotations that come directly from AspectJ packages.

First, we need to rely on the package AspectJweaver. Jar, which comes from AspectJ:

< the dependency > < groupId > org. Aspectj < / groupId > < artifactId > aspectjweaver < / artifactId > < version > 1.8.11 < / version > </dependency>Copy the code

If you are using Spring Boot, add the following dependencies:

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
Copy the code

In the @AspectJ configuration, AspectJweaver is introduced not because we need to use AspectJ’s processing capabilities, but because Spring uses some of the annotations provided by AspectJ and is really pure Spring AOP code.

Having said that, to be clear, @AspectJ uses annotations to configure the use of Spring AOP.

First, we need to turn on @AspectJ’s annotation configuration in two ways:

1. Configure in XML:

<aop:aspectj-autoproxy/>
Copy the code
  1. Using the @ EnableAspectJAutoProxy
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {

}
Copy the code

Once the above configuration is turned on, all beans annotated with @Aspect are treated by Spring as configuration classes to implement AOP, which we call an Aspect.

Note that to apply the @aspect annotation to a bean, either using @Component or configuring the bean in XML, it first needs to bea bean.

For example, the following bean, which uses @aspect on its class name, would be treated as a Spring AOP configuration.

<bean id="myAspect" class="org.xyz.NotVeryUsefulAspect"> <! -- configure properties of aspect here as normal --> </bean>Copy the code
package org.xyz;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class NotVeryUsefulAspect {

}
Copy the code

Next, we need to worry about what we need to configure in the @Aspect annotated bean.

First, we need to configure the Pointcut, which in most places is translated as a Pointcut that defines which methods need to be enhanced or blocked, similar to the method matching on the Advisor described earlier.

Spring AOP only supports methods in beans (not as powerful as AspectJ), so we can assume that Pointcut is designed to match methods of all beans in the Spring container.

@Pointcut("execution(* transfer(..) )")// the pointcut expression
private void anyOldTransfer() {}// the pointcut signature
Copy the code

Execution is the most commonly used match method signature in @pointcut. In addition to execution, let’s look at a few other common matches:

  • Within: Specifies the method under the class or package (unique to Spring AOP)

    Such as the @pointcut (” within (com) javadoop. Springaoplearning. Service.. *) “)

  • @annotation: Methods have specific annotations, such as @Subscribe to Subscribe to specific events.

    Such as the @pointcut (” execution (

    . * (..) ) && @annotation(com.javadoop.annotation.Subscribe)”)

  • Bean (idOrNameOfBean) : Matches the bean name (Spring AOP only)

    Service such as the @pointcut (” bean (*) “)

Tips: In the above match, usually “.” represents a package name, “..” Represents the package and its subpackages, and matches method arguments arbitrarily with two dots “..” .

Spring has a good suggestion for Web developers to define a SystemArchitecture:

@aspect public class SystemArchitecture @pointcut ("within(com.javadoop.web.. *)")
    public void inWebLayer() {} @pointcut () {"within(com.javadoop.service.. *)")
    public void inServiceLayer() {} @pointcut ();"within(com.javadoop.dao.. *)")
    public void inDataAccessLayer() {} // service implementation, note that this refers to the method implementation, but can also use bean(*ServiceImpl) @pointcut ()"execution(* com.javadoop.. service.*.*(..) )")
    public void businessService() {// dao implements @pointcut ()"execution(* com.javadoop.dao.*.*(..) )")
    public void dataAccessOperation() {}}Copy the code

The above architecture makes sense; this Aspect defines a bunch of pointcuts, which can then be referenced directly wherever pointcuts are needed (such as pointcut-ref=”” in XML).

Configuring the pointcut is to configure which methods we need to intercept. Next, we need to configure what we need to do with those intercepted methods, which is the Advice described earlier.

Next, we configure Advice.

The following code sample shows a variety of common cases:

Note that when you’re actually writing code, you don’t want to put all the facets into one class.

@aspect public class AdviceExample {// SystemArchitecture is used here"Dao Layer implementation"
    @Before("com.javadoop.aop.SystemArchitecture.dataAccessOperation()")
    public void doAccessCheck() {/ /... } // Of course, we can also directly"Inline"Advice and Pointcut are combined, but we should make a clear distinction between the two concepts: @before ("execution(* com.javadoop.dao.*.*(..) )")
    public void doAccessCheck() {/ /... Implementation code} @afterreturning ("com.javadoop.aop.SystemArchitecture.dataAccessOperation()")
    public void doAccessCheck() {/ /... } @AfterReturning( pointcut="com.javadoop.aop.SystemArchitecture.dataAccessOperation()",
        returning="retVal")
    public void doAccessCheck(retVal) {AccessCheck(retVal) {AccessCheck(retVal) { } // Return @afterthrowing ("com.javadoop.aop.SystemArchitecture.dataAccessOperation()")
    public void doRecoveryActions() {/ /... } @afterthrowing (pointcut="com.javadoop.aop.SystemArchitecture.dataAccessOperation()",
        throwing="ex")
    public void doRecoveryActions(DataAccessException ex) { // ... } // Understand the difference between @afterreturning and @afterreturning, which intercepts normal returns and exceptions in @after ("com.javadoop.aop.SystemArchitecture.dataAccessOperation()")
    public void doReleaseLock() {// Is usually used like a finally block to release resources. {return returning returning () {return returning () {return returning () {return returning () {return returning () {return returning () {return returning () {return returning ();"com.javadoop.aop.SystemArchitecture.businessService()")
    public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
        // start stopwatch
        Object retVal = pjp.proceed();
        // stop stopwatch
        returnretVal; }}Copy the code

Careful readers may have noticed that some Advice lacks method arguments, such as arguments that can be useful in @before scenarios where we want to log the input of intercepted methods.

Spring provides a very simple to get into the method of parameter, using org. Aspectj. Lang. JoinPoint as the first parameter to the Advice, such as:

@Before("com.javadoop.springaoplearning.aop_spring_2_aspectj.SystemArchitecture.businessService()")
public void logArgs(JoinPoint joinPoint) {
    System.out.println("Before executing the method, print the input parameter:" + Arrays.toString(joinPoint.getArgs()));
}
Copy the code

Note: first, must be placed on the first parameter; Second, if it is @around, we would normally use its subclass ProceedingJoinPoint because it has the procceed()/procceed(args[]) methods.

This completes the configuration of Pointcut and Advice in the @AspectJ configuration. Defining the Pointcut and using appropriate Advice on each Pointcut are the two most important things for developers.

Next, we use @AspectJ, introduced in this section, to implement the logging method parameters and logging method return values implemented in the previous section.

The configuration of XML is very simple:

This is an example, so beans are configured using XML.

Test it out:

Output result:

Before the method is executed, print the input parameter: [Tom, Cruise, 55] User{firstName='Tom', lastName='Cruise', age=55, address='null'} before the method is executed, print the input parameter: [] User{firstName='Tom', lastName='Cruise', age=55, address='null'}
Copy the code

JoinPoint has some other useful methods besides getArgs() that you can take a look at.

Finally, the configuration in @aspect does not apply to beans annotated with @Aspect.

Spring 2.0 Schema-based configuration

This section describes the < AOP /> namespace-based XML configuration provided after Spring 2.0. Schema-based refers to a schema based on AOP.

The IOC introduction also explained how Spring resolves namespaces (various * NamespaceHandlers), Parsing < aop / > source in org. Springframework. Aop. Config. AopNamespaceHandler.

Knowing how @AspectJ is configured, it’s pretty easy to understand how XML is configured, so we can cut the crap.

Configuration aspects are introduced here for subsequent understanding:

<aop:config>
    <aop:aspect id="myAspect" ref="aBean">... </aop:aspect> </aop:config> <bean id="aBean" class="...">... </bean>Copy the code

All configurations are under < AOP :config>.

A bean needs to be specified in < AOP: Aspect >, and as with LogArgsAspect and LogResultAspect introduced earlier, we know that we need to write processing code in this bean.

Then, once we have written the Aspect code, we “weave” it into the appropriate Pointcut, which is the Aspect.

Next, we need to configure Pointcut, which is as simple as this:

<aop:config>

    <aop:pointcut id="businessService"
        expression="execution(* com.javadoop.springaoplearning.service.*.*(..) )"/ > <! Aop :pointcut id="businessService2"
        expression="com.javadoop.SystemArchitecture.businessService()"/>

</aop:config>
Copy the code

Making < AOP :pointcut> a direct child of < AOP :config> will act as a global pointcut.

We can also configure the Pointcut inside < AOP :aspect /> so that the Pointcut applies only to that aspect:

<aop:config>
    <aop:aspect ref="logArgsAspect">
        <aop:pointcut id="internalPointcut"
                expression="com.javadoop.SystemArchitecture.businessService()" />
    </aop:aspect>
</aop:config>
Copy the code

Next, we should configure Advice. In order to avoid too much nonsense, let’s go directly to the example, which is quite understandable, and carry over the @aspectj configuration from the previous section:

In the example above, we configured two LogArgsAspect and one LogResultAspect.

Xml-based configurations are also very flexible, and while there is no way to show you the various combinations, you can easily configure them by holding on to basic Pointcut, Advice, and aspects.

summary

So far, this article has covered three ways to configure Spring AOP, but it’s important to remember that so far, we’ve been using Spring AOP and have nothing to do with AspectJ.

The next article will cover how AspectJ is used and how to use it in Spring applications. After almost ready to Spring AOP source analysis.

The appendix

The test source code used in this article has been uploaded to Github: Hongjiev/Spring-AOP-Learning.

It is recommended that you clone it and test it on the command line instead of relying on the IDE because it is too “smart” :

  1. mvn clean package

  2. Java – jar target/spring aop – learning – 1.0 – jar – with – dependencies. The jar

    Pom.xml is configured with the Assembly plug-in, which packs all jar package dependencies together.

  3. Modify the code in application.java, or other code, and repeat 1 and 2

(Full text)