关于spring:Spring-Boot-中的-AOP到底是-JDK-动态代理还是-Cglib-动态代理

20次阅读

共计 4003 个字符,预计需要花费 11 分钟才能阅读完成。

周六发了一个抽奖送书的流动,不过抽奖的人不多,中奖率蛮高的,小伙伴们能够去试试运气:

  • 指令重排序?代码不按写的程序执行吗?送书啦!

好啦,开始明天的注释。

大家都晓得,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 动静代理,须要本人手动配置。

just this。

正文完
 0