Spring|Spring Boot 中的 AOP,到底是 JDK 动态代理还是 Cglib 动态代理()
周六发了一个抽奖送书的活动,不过抽奖的人不多,中奖率蛮高的,小伙伴们可以去试试运气:
- 指令重排序?代码不按写的顺序执行吗?送书啦!
【Spring|Spring Boot 中的 AOP,到底是 JDK 动态代理还是 Cglib 动态代理()】大家都知道,AOP 底层是动态代理,而 Java 中的动态代理有两种实现方式:
- 基于 JDK 的动态代理
- 基于 Cglib 的动态代理
那么小伙伴们不禁要问,Spring 中的 AOP 是怎么实现的?是基于 JDK 的动态代理还是基于 Cglib 的动态代理?
1. Spring 先来说结论,Spring 中的动态代理,具体用哪种,分情况:
- 如果代理对象有接口,就用 JDK 动态代理,否则就是 Cglib 动态代理。
- 如果代理对象没有接口,那么就直接是 Cglib 动态代理。
文章图片
可以看到,即使在最新版的 Spring 中,依然是如上策略不变。即能用 JDK 做动态代理就用 JDK,不能用 JDK 做动态代理就用 Cglib,即首选 JDK 做动态代理。
2. Spring Boot Spring Boot 和 Spring 一脉相承,那么在动态代理这个问题上是否也是相同的策略呢?抱歉,这个还真不一样。
Spring Boot 中对这个问题的处理,以 Spring Boot2.0 为节点,前后不一样。
在 Spring Boot2.0 之前,关于 Aop 的自动化配置代码是这样的(Spring Boot
1.5.22.RELEASE
):@Configuration
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "https://www.it610.com/article/true", matchIfMissing = true)
public class AopAutoConfiguration {@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "https://www.it610.com/article/false",
matchIfMissing = true)
public static class JdkDynamicAutoProxyConfiguration {}@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "https://www.it610.com/article/true",
matchIfMissing = false)
public static class CglibAutoProxyConfiguration {}}
可以看到,这个自动化配置主要是在讨论 application.properties 配置文件中的
spring.aop.proxy-target-class
属性的值。具体起作用的是
@ConditionalOnProperty
注解,关于这个注解中的几个属性,松哥也来稍微说下:- prefix:配置文件的前缀。
- name:配置文件的名字,和 prefix 共同组成配置的 key。
- having:期待配置的值,如果实际的配置和 having 的值相同,则这个配置就会生效,否则不生效。
- matchIfMissing:如果开发者没有在 application.properties 中进行配置,那么这个配置类是否生效。
- 如果开发者设置了
spring.aop.proxy-target-class
为 false,则使用 JDK 代理。 - 如果开发者设置了
spring.aop.proxy-target-class
为 true,则使用 Cglib 代理。 - 如果开发者一开始就没配置
spring.aop.proxy-target-class
属性,则使用 JDK 代理。
再来看看 Spring Boot 2.0(含)之后的情况(Spring Boot
2.0.0.RELEASE
):@Configuration
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class,
AnnotatedElement.class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "https://www.it610.com/article/true", matchIfMissing = true)
public class AopAutoConfiguration {@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "https://www.it610.com/article/false", matchIfMissing = false)
public static class JdkDynamicAutoProxyConfiguration {}@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "https://www.it610.com/article/true", matchIfMissing = true)
public static class CglibAutoProxyConfiguration {}}
可以看到,大部分配置都是一样的,有一个地方不太相同,那就是 matchIfMissing 属性的值。可以看到,从 Spring Boot2.0 开始,如果用户什么都没有配置,那么默认情况下使用的是 Cglib 代理。
3. 实践 最后我们写一个简单的例子验证一下我们的想法。
首先创建一个 Spring Boot 项目(本案例使用最新版 Spring Boot,即默认使用 Cglib 代理),加入三个依赖即可,如下:
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-aop
接下来我们创建一个 IUserService 接口,如下:
public interface IUserService {
void hello();
}
然后我们再来创建一个该接口的实现类:
@Service
public class UserServiceImpl implements IUserService {
@Override
public void hello() {}
}
方法不用实现。
再来一个简单的切面:
@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());
}
}
最后再来一个简单的测试方法,注入 IUserService 实例:
@RestController
public class HelloController {
@Autowired
IUserService iUserService;
@GetMapping("/hello")
public void hello() {
iUserService.hello();
}
}
DBUEG 运行一下,就可以看到 IUserService 是通过 Cglib 来代理的。
文章图片
如果我们想用 JDK 来代理,那么只需要在 application.properties 中添加如下配置即可:
spring.aop.proxy-target-class=false
添加完成后,重新 DEBUG,如下图:
文章图片
可以看到,已经使用了 JDK 动态代理了。
如果用的是 Spring Boot 1.5.22.RELEASE 这个版本,那么即使不在 application.properties 中添加配置,默认也是 JDK 代理,这个我就不测试了,小伙伴们可以自己来试试。
4. 小结 总结一下:
- Spring 中的 AOP,有接口就用 JDK 动态代理,没有接口就用 Cglib 动态代理。
- Spring Boot 中的 AOP,2.0 之前和 Spring 一样;2.0 之后首选 Cglib 动态代理,如果用户想要使用 JDK 动态代理,需要自己手动配置。
推荐阅读
- 热闹中的孤独
- JS中的各种宽高度定义及其应用
- Activiti(一)SpringBoot2集成Activiti6
- 我眼中的佛系经纪人
- 《魔法科高中的劣等生》第26卷(Invasion篇)发售
- Android中的AES加密-下
- 放下心中的偶像包袱吧
- C语言字符函数中的isalnum()和iscntrl()你都知道吗
- SpringBoot调用公共模块的自定义注解失效的解决
- C语言浮点函数中的modf和fmod详解