【JDK 动态代理】 和 【CGLIB 代理】对比

目录

■前言

■问题分析

[1.【JDK 动态代理】 和 【CGLIB 代理】对比](#1.【JDK 动态代理】 和 【CGLIB 代理】对比)

2.什么时候使用CGLIB,什么时候使用JDK代理

[3.我的目标类有接口,但是使用了 实体类进行注入,这时会报错吗。](#3.我的目标类有接口,但是使用了 实体类进行注入,这时会报错吗。)

3.1.有疑问,我之前都不报错,今天pull代码后报错,你认为是哪里的配置修改造成了错误的发生

■临时对应策

[指定CGLIB代理后,没有默认构造函数的@Compoment类报错 No visible Constructors in class](#指定CGLIB代理后,没有默认构造函数的@Compoment类报错 No visible Constructors in class)

什么是CGLIB

■最终对应策

■发生原因(谁修改了什么)(感觉还是迷)

[什么情况下 默认 是 CGLIB, 不匹配时 JDK代理](#什么情况下 默认 是 CGLIB, 不匹配时 JDK代理)

■其它(查看Spring的版本)


■前言

之前代码注入对象时,没好好写,一个有接口的类,注入时,没有使用接口定义,而是使用实体类定义,虽然不符合规范,但是代码一直能跑起来。

今天pull共同的最新代码之后,代码报错了。

错误是:but was actually of type 'jdk.proxy4.$Proxy316'

===

■问题分析

1.【JDK 动态代理】 和 【CGLIB 代理】对比

===

对比维度 JDK 动态代理 CGLIB 代理
代理原理 基于接口,通过反射机制动态生成一个实现了目标接口的代理类 基于继承,通过 ASM 字节码框架动态生成目标类的子类
接口要求 必须有接口。只能代理接口中声明的方法 不需要接口 。可以代理普通类,但类和方法不能是 final
Spring 默认策略 如果目标 Bean 实现了至少一个接口,Spring AOP 默认使用 JDK 动态代理 如果目标 Bean 没有实现任何接口,Spring AOP 会自动切换为 CGLIB 代理
注入时的类型 注入的代理对象只能被赋值给其实现的接口类型,不能赋值给实现类类型(否则会抛异常) 注入的代理对象是目标类的子类,因此既可以 赋值给接口,也可以赋值给目标类本身
能否代理实体类 不能。实体类(如 POJO)通常不实现接口,JDK 代理无法处理 可以 。只要实体类不是 final 的,且方法不是 final 的,CGLIB 就可以生成子类进行代理
主要限制 只能增强接口中定义的方法,无法增强实现类特有的 public 方法 无法代理 final 类、final 方法、private 方法

===

2.什么时候使用CGLIB,什么时候使用JDK代理

===

配置目标 具体配置方式 生效环境 实际代理结果
强制固化使用 CGLIB Spring Boot:spring.aop.proxy-target-class=true Spring Boot 无论有无接口,一律 CGLIB
原生 Spring:@EnableAspectJAutoProxy(proxyTargetClass = true) 原生 Spring 无论有无接口,一律 CGLIB
XML:<aop:aspectj-autoproxy proxy-target-class="true"/> XML 配置项目 无论有无接口,一律 CGLIB
"强制 JDK"/"不固化,根据情况自己选择"(两者实际行为一致) Spring Boot:spring.aop.proxy-target-class=false Spring Boot 有接口 → JDK;无接口 → CGLIB 兜底
原生 Spring:@EnableAspectJAutoProxy(proxyTargetClass = false) 显式写 false 原生 Spring 有接口 → JDK;无接口 → CGLIB 兜底
原生 Spring:@EnableAspectJAutoProxy 不写 proxyTargetClass(默认值即 false) 原生 Spring 有接口 → JDK;无接口 → CGLIB 兜底
原生 Spring:proxyTargetClass 保持默认 false(不设置) 原生 Spring 有接口 → JDK;无接口 → CGLIB 兜底
不设置任何 proxy-target-class / proxyTargetClass 配置 原生 Spring(默认状态) 动态判断:有接口 → JDK;无接口 → CGLIB
不设置任何代理配置 Spring Boot 2.x+(注意差异) Boot 自动配置默认已固化为 CGLIB,不设置也是 CGLIB;需显式设 spring.aop.proxy-target-class=false 才退回"有接口 JDK、无接口 CGLIB"的动态逻辑

====

3.我的目标类有接口,但是使用了 实体类进行注入,这时会报错吗。

==

**如果,****(注意有前提条件,如果)**实际生成的是 JDK 动态代理:一定报错

JDK 动态代理生成的 Proxy 类只实现了目标类所实现的接口,它不是目标实体类的子类。因此,这个 Proxy 对象在类型上与你的实体类不兼容。当 Spring 试图把这个代理对象注入到一个声明为实体类类型的字段时,就会因为类型不匹配而抛出异常,你看到的 $Proxy 报错正是由此而来。

===

报错:but was actually of type 'jdk.proxy4.$Proxy316' // 316 只是一个数字,还可以是其它值

===

实际使用的代理类型 代理对象与实体类的关系 按实体类注入的结果 触发条件(配置/环境)
JDK 动态代理 $Proxy 只实现接口,不是实体类的子类 报错 (类型不匹配,如 $Proxy 无法赋值给实体类) ① Spring Boot 配了 spring.aop.proxy-target-class=false;② 原生 Spring 未强制 CGLIB(proxyTargetClass 为 false/未设置)且目标类有接口;③ XML 配 proxy-target-class="false"
CGLIB 代理 代理对象是实体类的子类 不报错(可安全赋值给实体类类型) ① Spring Boot 默认(spring.aop.proxy-target-class=true 或未改默认值);② 原生 Spring @EnableAspectJAutoProxy(proxyTargetClass = true);③ XML 配 proxy-target-class="true"

=====

3.1.有疑问,我之前都不报错,今天pull代码后报错,你认为是哪里的配置修改造成了错误的发生

====

可能的改动 影响
Spring Boot 的 application.properties/yml 里新增或改成了 spring.aop.proxy-target-class=false Boot 默认是 CGLIB,改成 false 后对有接口的类改用 JDK 代理,直接触发你的报错
原生 Spring 项目里,@EnableAspectJAutoProxy 的 proxyTargetClass = true 被删掉或改成了 false 有接口的类从 CGLIB 退回 JDK 代理
XML 配置里 <aop:aspectj-autoproxy proxy-target-class="true"/> 被删或改成 false 同上
引入了新的 starter / 自动配置,覆盖了原来的 AOP 代理设置 例如某些安全、事务相关的自动配置会重新定义 proxy-target-class

=====

■临时对应策

====

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

/**
 * 配置类:强制使用 CGLIB 代理
 * proxyTargetClass = true 表示无论目标类是否有接口,都基于类生成子类代理
 */
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppConfig {

    // 目标 Bean:一个没有实现任何接口的普通类(类似实体类/POJO)
    @Bean
    public OrderService orderService() {
        return new OrderService();
    }

    // 切面 Bean
    @Bean
    public LogAspect logAspect() {
        return new LogAspect();
    }
}

===

指定CGLIB代理后,没有默认构造函数的@Compoment类报错 No visible Constructors in class

====

指定CGLIB代理后,没有默认构造函数的@Compoment类报错 No visible Constructors in class

你遇到的 No visible Constructors in class 报错,根本原因是:CGLIB 通过生成目标类的子类来实现代理,而生成这个子类时,需要能够访问到父类(也就是你的 @Component 类)的某个构造方法。 当你的类没有任何可见构造方法(即没有 public、protected 或包级私有的构造方法,只有 private 构造方法)时,CGLIB 无法生成子类,就会抛出这个异常。

===

为什么指定 CGLIB 后才会触发

在 JDK 动态代理模式下,代理对象是 $Proxy,它只实现接口,不需要继承你的类,所以你的类构造方法是否可见完全不影响代理的生成。一旦你强制使用 CGLIB,Spring 就必须生成你的类的子类,此时 CGLIB 需要调用父类的构造方法来完成子类的实例化过程,构造方法的可见性就成了硬性门槛。

===

什么是CGLIB

==

CGLIB(Code Generation Library)是 Spring AOP 在需要代理普通类(尤其是没有实现接口的类)时使用的底层技术。它的核心是在运行时动态生成目标类的一个子类 ,并通过重写方法来插入增强逻辑。你之前遇到的"按实体类注入成功"以及"No visible Constructors 报错",根源都在于这套"继承"机制。

=====

■最终对应策

====

规范代码,都使用接口声明

===

■发生原因(谁修改了什么)(感觉还是迷)

====

新增或改成了 spring.aop.proxy-target-class=false

默认是 CGLIB,改成 false 后对有接口的类改用 JDK 代理,直接触发报错

错误是:but was actually of type 'jdk.proxy4.$Proxy316'

===

什么情况下 默认 是 CGLIB, 不匹配时 JDK代理

====

===

■其它(查看Spring的版本)

==

java 复制代码
import org.springframework.core.SpringVersion;
import org.springframework.boot.SpringBootVersion;

public class VersionPrinter implements CommandLineRunner {
    @Override
    public void run(String... args) {
        System.out.println("Spring Framework Version: " + SpringVersion.getVersion());
        // 如果是 Spring Boot 项目,也可以同时打印 Boot 版本
        System.out.println("Spring Boot Version: " + SpringBootVersion.getVersion());
    }
}

===

相关推荐
文丑颜不良啊1 小时前
LeetCode1261~1280题目解析
java·leetcode
郭小李1 小时前
spring 中的 @EnableWebMvc注解和WebMvcConfigurer接口
java·后端·spring
YH_Analytics1 小时前
2026 高压 E-House 市场深度分析报告市场格局
网络·数据库
后台模板学习1 小时前
MongoDB 与 MySQL 在 IM 即时聊天场景中该
数据库·mysql·mongodb
香蕉大香蕉(06敬畏市场)2 小时前
高频八股 · JVM(8)
java·jvm·面试·八股·大厂实习
养军博客2 小时前
spirngSecurity 鉴权教程02
java·springsecurity
CoderYanger2 小时前
Java EE 进阶:5. Spring IoC&DI(第二弹):Spring IoC
java·开发语言·spring boot·spring·面试·职场和发展·java-ee
KaiwuDB2 小时前
企业级数据库数据保护:KaiwuDB KaiwuDB V3.3.0 发布:S3 冷热分层存储与主备集群能力增强实战解析模场景备份恢复方案解析
数据库·时序数据库·kaiwudb·发版·多模时序数据库·冷热分层存储·主备集群
DataY2 小时前
MySQL CDC 直接写进 Doris 汇总模型:同步即汇总,明细不用搬两遍
数据库