As we all know, the underlying AOP is dynamic proxies, and there are two ways to implement dynamic proxies in Java:

  • JDK-based dynamic proxy
  • Dynamic proxy based on Cglib

The biggest difference between these two is that JDK-based dynamic proxies require the object being proxied to implement an interface, while Cglib-based dynamic proxies do not require the object being proxied to implement an interface.

So, how is AOP implemented in Spring? Is it a dynamic proxy based on JDK or a dynamic proxy based on Cglib?

1. Spring

Let’s start with the conclusion that dynamic proxies in Spring, which one to use, are divided into cases.

  • If the proxy object implements the interface, then use the JDK dynamic proxy, otherwise it is the Cglib dynamic proxy.
  • If the proxy object does not implement an interface, then it is a direct Cglib dynamic proxy.

Let’s take a look at the official documentation.

spring aop

As you can see, even in the latest version of Spring, the strategy remains the same as above. That is, if you can use JDK to do dynamic proxy, use JDK, if you can’t use JDK to do dynamic proxy, use Cglib, that is, JDK is preferred to do dynamic proxy.

2. Spring Boot

Spring Boot and Spring are the same, so is it the same strategy for dynamic proxies? Sorry, it’s not really the same.

The handling of this issue in Spring Boot, with Spring Boot 2.0 as the node, is not the same.

Before Spring Boot 2.0, the code for automating the configuration of Aop looked like this (Spring Boot 1.5.22.RELEASE)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Configuration
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {

	@Configuration
	@EnableAspectJAutoProxy(proxyTargetClass = false)
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
			matchIfMissing = true)
	public static class JdkDynamicAutoProxyConfiguration {

	}

	@Configuration
	@EnableAspectJAutoProxy(proxyTargetClass = true)
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
			matchIfMissing = false)
	public static class CglibAutoProxyConfiguration {

	}

}

As you can see, this automation configuration is mainly discussing the value of the spring.aop.proxy-target-class property in the application.properties configuration file.

The @ConditionalOnProperty annotation is what does the trick. To illustrate a few of the properties in this annotation.

  • prefix: The prefix of the configuration file.
  • name: the name of the configuration file, and prefix together form the key of the configuration.
  • having: the value of the expected configuration. If the actual configuration is the same as the value of having, then the configuration will take effect, otherwise it will not.
  • matchIfMissing: if the developer did not configure it in application.properties, then this configuration class will take effect or not.

Based on the introduction as above, it is easy to see that.

  • If the developer has set spring.aop.proxy-target-class to false, then the JDK proxy is used.
  • If the developer has spring.aop.proxy-target-class set to true, then the Cglib proxy is used.
  • If the developer did not configure the spring.aop.proxy-target-class property in the first place, then the JDK proxy is used.

This was the case before Spring Boot 2.0.

Let’s look at the situation after Spring Boot 2.0 (inclusive) (Spring Boot 2.0.0.RELEASE).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
@Configuration
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class,
		AnnotatedElement.class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {

	@Configuration
	@EnableAspectJAutoProxy(proxyTargetClass = false)
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
	public static class JdkDynamicAutoProxyConfiguration {

	}

	@Configuration
	@EnableAspectJAutoProxy(proxyTargetClass = true)
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
	public static class CglibAutoProxyConfiguration {

	}

}

As you can see, most of the configuration is the same, with one area that is not quite the same, and that is the value of the matchIfMissing property. As you can see, starting with Spring Boot 2.0, if the user does not configure anything, the Cglib proxy is used by default.

3. Practicing

Finally, let’s write a simple example to verify our ideas.

First create a Spring Boot project (in this case using the latest version of Spring Boot, i.e. using the Cglib proxy by default) and just add three dependencies, as follows.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

Next we create an IUserService interface, as follows.

1
2
3
public interface IUserService {
    void hello();
}

Let’s then create an implementation class for that interface.

1
2
3
4
5
6
7
@Service
public class UserServiceImpl implements IUserService {
    @Override
    public void hello() {

    }
}

method does not need to be implemented.

Write another simple Aspect.

1
2
3
4
5
6
7
8
9
@EnableAspectJAutoProxy
@Aspect
@Component
public class LogAspect {
    @Before("execution(* org.javaboy.demo.UserServiceImpl.*(..))")
    public void before(JoinPoint jp) {
        System.out.println("jp.getSignature().getName() = " + jp.getSignature().getName());
    }
}

Finally, write a simple test method that injects an instance of IUserService.

1
2
3
4
5
6
7
8
9
@RestController
public class HelloController {
    @Autowired
    IUserService iUserService;
    @GetMapping("/hello")
    public void hello() {
        iUserService.hello();
    }
}

Execute the test. With DBUEG, you can see that IUserService is proxied by Cglib.

aop test

If we want to use the JDK as a proxy, then we just need to add the following configuration to application.properties.

1
spring.aop.proxy-target-class=false

After adding properties, re DEBUG. the result is as follows.

aop test

As you can see, the JDK dynamic proxy is already in use.

If you are using Spring Boot 1.5.22.RELEASE, then even if you don’t add configuration to application.properties, the default is a JDK proxy. I won’t test this, but you can try it yourself if you are interested.

4. Summary

  1. AOP in Spring, if you have an interface, use the JDK dynamic proxy, no interface, use Cglib dynamic proxy.
  2. Spring Boot AOP, before 2.0 and Spring the same; 2.0 after the preferred Cglib dynamic proxy, if users want to use JDK dynamic proxy, you need to manually configure their own.

Refrence https://juejin.cn/post/7035880349422845960