问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

Spring Boot中的AOP:JDK动态代理还是Cglib动态代理?

创作时间:
作者:
@小白创作中心

Spring Boot中的AOP:JDK动态代理还是Cglib动态代理?

引用
CSDN
1.
https://blog.csdn.net/lgq2016/article/details/136650501

大家都知道,AOP底层是动态代理,而Java中的动态代理有两种实现方式:

  • 基于JDK的动态代理
  • 基于Cglib的动态代理

这两者最大的区别在于基于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 = "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 {
    }
}

可以看到,这个自动化配置主要是在讨论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(含)之后的情况(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 {
    }
}

可以看到,大部分配置都是一样的,有一个地方不太相同,那就是matchIfMissing属性的值。可以看到,从Spring Boot2.0开始,如果用户什么都没有配置,那么默认情况下使用的是Cglib代理。

3. 实践

最后我们写一个简单的例子验证一下我们的想法。

首先创建一个Spring Boot项目(本案例使用最新版Spring Boot,即默认使用Cglib代理),加入三个依赖即可,如下:

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

接下来我们创建一个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. 小结

总结一下:

  1. Spring中的AOP,有接口就用JDK动态代理,没有接口就用Cglib动态代理。
  2. Spring Boot中的AOP,2.0之前和Spring一样;2.0之后首选Cglib动态代理,如果用户想要使用JDK动态代理,需要自己手动配置。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号