amazing! Spring5 AOP uses Cglib by default? From phenomenon to source code depth analysis

Does Spring5 AOP use Cglib by default? The first time I heard this statement was in a WeChat group:
group chat

real or fake? Check out the documentation

When I first saw this statement, I was skeptical.

We all know that the AOP version before Spring5 uses JDK dynamic proxy by default. Is it true that the Spring5 version has been modified? So I opened the Spring Framework 5.x document and confirmed it again:

Document address: https://docs.spring.io/spring/docs/5.2.0.RELEASE/spring-framework-reference/core.html#aop

Spring Framework 5.x Documentation
Simply translate. Spring AOP uses JDK dynamic proxy by default, and CGLIB proxy if the object does not implement the interface. Of course, it is also possible to force the use of CGLIB proxies.

What? Documentation wrong? !

After I sent the official document to the group, I received a reply from this classmate:
Documentation wrong?  !

SpringBoot 2.x code sample

In order to prove that the document was written wrong, this student also wrote a DEMO. Next, let me reproduce this DEMO program:

Operating environment: SpringBoot 2.2.0.RELEASE version, the built-in Spring Framework version is 5.2.0.RELEASE version. At the same time, add spring-boot-starter-aop dependency to automatically assemble Spring AOP.

public interface UserService {
    
    
    void work();
}

@Service
public class UserServiceImpl implements UserService {
    
    

    @Override
    public void work() {
    
    
        System.out.println("开始干活...coding...");
    }
}
@Component
@Aspect
public class UserServiceAspect {
    
    
    @Before("execution(* com.me.aop.UserService.work(..))")
    public void logBefore(JoinPoint joinPoint) {
    
    
        System.out.println("UserServiceAspect.....()");
    }
}

Is Cglib proxy used by default?
UserServiceImplThe interface is implemented UserService, and the pre-enhanced interception of the method UserServiceAspectis used at the same time.UserService#work

Judging from the running results, the CGLIB proxy is indeed used here instead of the JDK dynamic proxy.

Is it really a typo in the documentation? !

@EnableAspectJAutoProxy source code annotation

In the Spring Framework, @EnableAspectJAutoProxyannotations are used to enable Spring AOP-related functions.

The source code of the Spring Framework 5.2.0.RELEASE version @EnableAspectJAutoProxyannotation is as follows:
@EnableAspectJAutoProxy source code
Through the source code annotation, we can learn that: in the Spring Framework 5.2.0.RELEASE version, proxyTargetClassthe default value is still false, and the JDK dynamic proxy is still used by default.

Are the documentation and source code comments wrong? !

The proxyTargetClass of @EnableAspectJAutoProxy is invalid?

Next, I tried @EnableAspectJAutoProxyto force the use of the JDK dynamic proxy with

Operating environment: SpringBoot 2.2.0.RELEASE version, the built-in Spring Framework version is 5.2.0.RELEASE version.

The proxyTargetClass setting is invalid?
By running the discovery, the CGLIB proxy is still used. Is the proxyTargetClass setting of @EnableAspectJAutoProxy invalid?

Spring Framework 5.x

Organize your thoughts

  1. Some people say that Spring 5 starts AOP and uses CGLIB by default.
  2. Both the Spring Framework 5.x documentation and @EnableAspectJAutoProxysource code comments say that the default is to use JDK dynamic proxy
  3. The result of the program running shows that even if the interface is inherited and set proxyTargetClassto false, the program still uses the CGLIB proxy

Wait a minute, are we missing something?

The sample program is run using SpringBoot, so what if you only use Spring Framework instead of SpringBoot?

Operating environment: Spring Framework 5.2.0.RELEASE version. The UserServiceImpl and
UserServiceAspect classes are the same as above, so I won't repeat them here.

Spring Framework 5.x
insert image description here
The running results show that: In the Spring Framework 5.x version, if the class implements the interface, AOP still uses the JDK dynamic proxy by default.

Rearrange ideas

  1. Spring5 AOP still uses JDK dynamic proxy by default, and the official documents and source code comments are correct.
  2. In SpringBoot 2.x version, AOP uses cglib by default and cannot be modified through proxyTargetClass.
  3. Did the SpringBoot 2.x version make some changes?

Explore SpringBoot 2.x again

As a result of the above analysis, it is very likely that in the SpringBoot2.x version, the relevant configuration of Spring AOP has been modified. Then come to a wave of source code analysis to see what is going on inside.

Source code analysis

For source code analysis, it is very important to find the right entry. Where is the entrance this time?
@SpringBootApplicationis a composite annotation that uses @EnableAutoConfigurationa lot of autowiring.

EnableAutoConfigurationAlso a composite annotation, on which the annotation is flagged @Import. For @Importdetailed usage of annotations, please refer to the author's previous article: https://mp.weixin.qq.com/s/7arh4sVH1mlHE0GVVbZ84Q

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    
    

AutoConfigurationImportSelectorImplemented DeferredImportSelectorthe interface.

In the Spring Framework 4.x version, this is an empty interface, it just inherits ImportSelectorthe interface. In the 5.x version, DeferredImportSelectorthe interface is extended and a method is added getImportGroup: the class
AutoConfigurationImportSelector#getImportGroup
is returned in this method AutoConfigurationGroup. This is AutoConfigurationImportSelectoran inner class in , which implements DeferredImportSelector.Groupthe interface.

In the SpringBoot 2.x version, AutoConfigurationImportSelector.AutoConfigurationGroup#processthe automatic configuration class is imported through the method.
Import configuration class
Through breakpoint debugging, we can see that the automatic configuration related to AOP is org.springframework.boot.autoconfigure.aop.AopAutoConfigurationconfigured through .
AopAutoConfiguration source code

the truth came out

Seeing this, it can be said that the truth is revealed. In the SpringBoot2.x version, AopAutoConfigurationAOP is automatically assembled through.

By default, there is definitely no spring.aop.proxy-target-classsuch configuration item. At this time, Cglib will be used by default in SpringBoot 2.x version.

How to modify AOP implementation in SpringBoot 2.x

We can also know through the source code that if you need to modify the AOP implementation in SpringBoot 2.x, you need to spring.aop.proxy-target-classmodify it through this configuration item.

#在application.properties文件中通过spring.aop.proxy-target-class来配置
spring.aop.proxy-target-class=false

spring-configuration-metadata.json
Here is also a mention spring-configuration-metadata.jsonof the role of the file: when using application.propertiesor application.ymlfiles, IDEA provides code hints by reading the information of these files, and the SpringBoot framework itself will not read this configuration file.

What about SpringBoot 1.5.x

SringBoot 1.5.x
It can be seen that in the SpringBoot 1.5.x version, the JDK dynamic proxy is still used by default.

Why SpringBoot 2.x uses Cglib by default

Why does SpringBoot 2.x use Cglib to implement AOP by default? What are the benefits of doing so? The author found some information from the Internet, let's look at an issue first.

Spring Boot issue #5423

Use @EnableTransactionManagement(proxyTargetClass = true) #5423
https://github.com/spring-projects/spring-boot/issues/5423

In this issue, such a question is thrown:
issue

To translate: we should use @EnableTransactionManagement(proxyTargetClass =
true) to prevent nasty proxy issues when people don't use the interface.

What is this "nasty proxy problem when not using an interface"? Think for a minute.

nasty proxy problem

Suppose, we have a UserServiceImpland UserServiceclass, which needs to UserContollerbe used in this time UserService. In Spring, it is usually customary to write code like this:

@Autowired
UserService userService;

In this case, neither the JDK dynamic proxy nor CGLIB will cause problems.

But what if your code looks like this:

@Autowired
UserServiceImpl userService;

At this time, if we use the JDK dynamic proxy, an error will be reported at startup:
start error
because the JDK dynamic proxy is based on the interface, the objects generated by the proxy can only be assigned to interface variables.

CGLIB does not have this problem. Because CGLIB is realized by generating subclasses, whether the proxy object is assigned to the interface or the implementation class, both are the parent class of the proxy object.

For this reason, SpringBoot changed the default implementation of AOP to CGLIB in version 2.x.

For more details, readers can refer to the above issue by themselves.

Summarize

  1. AOP in Spring 5.x still uses JDK dynamic proxy by default.
  2. Starting from SpringBoot 2.x, CGLIB is used by default in order to solve the type conversion exceptions that may be caused by using JDK dynamic proxy.
  3. In SpringBoot 2.x, if you need to use JDK dynamic proxy by default, you can modify it through the configuration item spring.aop.proxy-target-class=false, and the proxyTargetClass configuration is invalid.

further reading

issue:Default CGLib proxy setting default cannot be overridden by using core framework annotations (@EnableTransactionManagement, @EnableAspectJAutoProxy) #12194
https://github.com/spring-projects/spring-boot/issues/12194

This issue also talked about proxyTargetClassthe issue of setting invalidation. The discussions include: @EnableAspectJAutoProxy, @EnableCachingand @EnableTransactionManagement. Interested readers can refer to the content of this issue by themselves.

References

https://www.cnblogs.com/coderxiaohei/p/11758239.html

Guess you like

Origin blog.csdn.net/sdujava2011/article/details/131432976