Is AOP in Spring Boot JDK dynamic proxy or Cglib dynamic proxy?

There was a lucky draw to send books on Saturday, but there were not many people in the lucky draw, and the winning rate was quite high. Friends can try their luck:

Well, let's start today's text.

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

  • Dynamic agent based on JDK
  • Dynamic agent based on Cglib

The biggest difference between the two is that the JDK based dynamic proxy needs the proxy object to have an interface, while the Cglib based dynamic proxy does not need the proxy object to have an interface.

So my friends can't help asking, how is AOP implemented in Spring? Is it a JDK based dynamic agent or a Cglib based dynamic agent?

1. Spring

Let's start with the conclusion. Which dynamic proxy is used in Spring, according to the situation:

  • If the proxy object has an interface, use JDK dynamic proxy, otherwise it is Cglib dynamic proxy.
  • If the proxy object has no interface, it is the Cglib dynamic proxy directly.

Take a look at this statement from official documents:

You can see that even in the latest version of Spring, the above strategy remains unchanged. That is, use JDK if you can use JDK as a dynamic agent, and use Cglib if you can't use JDK as a dynamic agent, that is, JDK is preferred as a dynamic agent.

2. Spring Boot

Spring Boot and spring come down in one continuous line. Is it the same strategy on the issue of dynamic proxy? Sorry, this is really different.

The handling of this problem in Spring Boot takes Spring Boot2.0 as the node, which is different.

Before spring boot 2.0, the automatic configuration code for Aop was as follows (Spring Boot 1.5.22.RELEASE):

@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 mainly discusses the value of the spring.aop.proxy-target-class property in the application.properties configuration file.

Specifically, the @ ConditionalOnProperty annotation works. SongGe will also say a little about several properties in this annotation:

  • Prefix: prefix of the configuration file.
  • Name: the name of the configuration file. Together with prefix, it forms the configuration key.
  • Having: the expected value of the configuration. If the actual configuration is the same as having, the configuration will take effect. Otherwise, it will not take effect.
  • matchIfMissing: if the developer does not configure it in application.properties, whether this configuration class is effective.

Based on the above introduction, we can easily see that:

  • If the developer sets spring.aop.proxy-target-class to false, the JDK proxy is used.
  • If the developer sets spring.aop.proxy-target-class to true, the Cglib proxy is used.
  • If the developer does not configure the spring.aop.proxy-target-class attribute at the beginning, the JDK proxy is used.

This was the case before Spring Boot 2.0.

Let's take a look at the situation after Spring Boot 2.0 (including Spring Boot 2.0.0.RELEASE):

@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 {

 }

}

You can see that most configurations are the same, but one thing is different, that is, the value of matchIfMissing attribute. As you can see, starting from spring boot 2.0, if the user does not configure anything, the Cglib proxy is used by default.

3. Practice

Finally, we write a simple example to verify our idea.

First, create a Spring Boot project (the latest version of Spring Boot is used in this case, that is, Cglib proxy is used by default), and add three dependencies, as follows:

<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:

public interface IUserService {
    void hello();
}

Then we will create an implementation class of the interface:

@Service
public class UserServiceImpl implements IUserService {
    @Override
    public void hello() {

    }
}

Method does not need to be implemented.

Another simple cut:

@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, another simple test method is injected into the IUserService instance:

@RestController
public class HelloController {
    @Autowired
    IUserService iUserService;
    @GetMapping("/hello")
    public void hello() {
        iUserService.hello();
    }
}

After running DBUEG, you can see that IUserService is represented through Cglib.

If we want to use JDK to proxy, we only need to add the following configuration in application.properties:

spring.aop.proxy-target-class=false

After adding, DEBUG again, as shown in the following figure:

As you can see, JDK dynamic proxy has been used.

If Spring Boot 1.5.22.RELEASE is used, even if the configuration is not added in application.properties, the default is JDK agent. I won't test this. My friends can try it by themselves.

4. Summary

To sum up:

  1. For AOP in Spring, JDK dynamic proxy is used if there is an interface, and Cglib dynamic proxy is used if there is no interface.
  2. AOP in Spring Boot is the same as Spring before 2.0; Cglib dynamic agent is preferred after 2.0. If users want to use JDK dynamic agent, they need to configure it manually.

just this.

Added by morphboy23 on Sat, 04 Dec 2021 08:22:32 +0200