SpringBoot/Spring AOP默认动态代理方式

Spring Boot不同版本AOP代理方式解析
本文介绍了Spring Boot不同版本AOP的代理方式。Spring 5.x中AOP默认用JDK动态代理,Spring Boot 1.x AOP默认也用JDK动态代理,而Spring Boot 2.x及以上版本为解决类型转换异常,AOP默认使用CGLIB代理,也可通过配置修改,还说明了JDK和CGLIB代理的特点。
  • Spring 5.x中AOP默认依旧使用JDK动态代理
  • SpringBoot 2.x开始,AOP为了解决使用JDK动态代理可能导致的类型转换异常,而使用CGLIB。
  • 在SpringBoot 2.x中,AOP如果需要替换使用JDK动态代理可以通过配置项spring.aop.proxy-target-class=false来进行修改,proxyTargetClass配置已无效。

1. springboot 2.x 及以上版本 

        在 SpringBoot 2.x AOP中会默认使用Cglib来实现,但是Spring5中默认还是使用jdk动态代理。Spring AOP 默认使用 JDK 动态代理,如果对象没有实现接口,则使用 CGLIB 代理。当然,也可以强制使用 CGLIB 代理。

在 SpringBoot 中,通过AopAutoConfiguration来自动装配AOP.

2. Springboot 1.x

Springboot 1.x AOP默认还是使用 JDK 动态代理的

 3.SpringBoot 2.x 为何默认使用 Cglib

        因为JDK 动态代理是基于接口的,代理生成的对象只能赋值给接口变量。JDK动态代理使用Proxy.newProxyInstance()创建代理实现类,然而第二个参数就需要接口类型,如果没有接口类型就会报错。

Proxy.newProxyInstance(iCustomerInstance.getClass().getClassLoader(), iCustomerInstance.getClass().getInterfaces(), this);

而 CGLIB 就不存在这个问题。因为 CGLIB 是通过生成子类来实现的,代理对象无论是赋值给接口还是实现类,这两者都是代理对象的父类。

所以在2.x版本以上,将 AOP 默认实现改为了 CGLIB代理。

新建一个接口

public interface ICustomService {

    void printf();
}

新建一个ICustomService的实现类

@Service
public class CustomServiceImpl implements ICustomService {

    public void printf() {

    }
}

再增加一个类不实现任何接口

@Service
public class CustomNoImpl {

    public void hello() {
        
    }
}

然后启动,可以ICustomService和CustomNoImpl看出AOP的代理使用的是CGLIB的动态代理

然后我们通过application.properties配置将代理默认设置为JDK代理。

spring.aop.proxy-target-class=false

    然后启动调试发现,CustomNoImpl因为没有实现接口,所以使用的是CGLIB生成的代理,而

customService有接口实现,所以使用JDK的动态代理

总结

JDK动态代理基于接口(必须声明接口并实现接口)

CGLIB基于子类,继承关系。(可以只定义类)。所以如果类是申明了final,就不能被CGLIB动态代理。比如webflux框架中的WebClient,就不能被AOP切面。

### Spring Boot 中使用 JDK 动态代理实现 AOP 为了在 Spring Boot 应用程序中利用 JDK 动态代理来实现面向切面编程 (AOP),应用程序中的目标对象必须实现至少一个接口。这是因为 JDK 动态代理仅能为目标对象的接口创建代理实例[^2]。 #### 添加必要的依赖项 要启用 AOP 支持,在 `pom.xml` 文件中引入如下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> ``` #### 配置 Aspect 类 定义一个切面类,该类用于声明通知逻辑。下面是一个简单的例子,展示了如何编写前置增强处理: ```java import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect @Component public class LoggingAspect { @Before("execution(* com.example.service..*(..))") public void logBefore() { System.out.println("Method is running"); } } ``` 在这个例子中,任何位于 `com.example.service` 包下的方法调用都会触发日志消息打印操作。 #### 创建服务接口和服务实现类 由于 JDK 动态代理只支持对接口的方法进行拦截,因此需要先定义一个接口以及其实现类: ```java // 定义业务接口 package com.example.service; public interface MyService { String performTask(); } // 接口的具体实现 package com.example.service.impl; import org.springframework.stereotype.Service; @Service public class MyServiceImpl implements MyService { @Override public String performTask() { return "Executing task"; } } ``` 上述代码片段展示了一个名为 `MyService` 的接口及其具体实现 `MyServiceImpl`。只有当目标组件实现了某个接口时,才能确保 Spring 使用的是基于此接口的 JDK 动态代理机制而不是 CGLIB 字节码生成技术。 #### 启动应用并测试功能 完成以上设置之后,启动 Spring Boot 应用即可看到预期的行为——即每当有匹配路径的方法被执行前会输出相应的日志信息。 需要注意的是,默认情况下 Spring Boot 可能会选择 CGLIB 来代替 JDK 动态代理除非明确指定了 proxyTargetClass=false 或者目标 Bean 已经实现了某些接口[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值