Spring 内部类获取不到@Value配置值问题排查(附Spring代理方式)

news2024/12/26 9:27:31

目录

一、实例问题

1、现象

2、原因

3、解决

二、Spring的代理模式

1、静态代理(Static Proxy)

1)原理

2)优缺点

3)代码实现

2、JDK动态代理(JDK Dynamic Proxy)

1)原理

2)优缺点

3)代码实现

3、cglib 代理(Code Generation Library Proxy)

1)原理

2)优缺点

3)代码实现


一、实例问题

1、现象

业务场景中出现过使用内部类获取属性的时候获取不到,导致业务逻辑执行异常,观察代码后发现原因是最底层有@Async的方法导致Spring代理方式变更,特此梳理一下。

如下代码结果展示

类代码


/**
 * @author YY-帆S
 * @Date 2024/4/16 21:16
 */
@Service
@Slf4j
public class TestProxy {

    @Value("${test.boolean:true}")
    public boolean aBoolean;

    public void testAQuery() {
        log.info("testAQuery:{}", aBoolean);
        TestProxy bean = SpringContextUtils.getBean(TestProxy.class);
        bean.testBQuery();
        bean.testCQuery();
    }

    public void testBQuery() {
        log.info("testBQuery:{}", aBoolean);
    }

    private void testCQuery() {
        log.info("testCQuery:{}", aBoolean);
    }

    @Component
    public class InnerClass {
        public void testInnerQuery() {
            log.info("testInnerQuery:{}", aBoolean);
        }
    }

//第一次 不加上
//  @Async
//第二次加上
    @Async
    public void testAsync() {
    }
}

 调用代码

    @Resource
    TestProxy testProxy;

    @Resource
    TestProxy.InnerClass innerClass;

    @GetMapping("testQuery")
    public CommonResult<Object> testQuery() {
        testProxy.testAQuery();
        innerClass.testInnerQuery();
        return new CommonResult<>();
    }

不加上@Asnyc的结果

加上@Async的结果

2、原因

加Async之前的bean属性,内部aBoolean=true

加Async之后的bean,内部aBoolean=false,(默认值)

可以观察到,加@Async或@Transcational 这类的注解时,会导致spring切换对类切换代理方式,为cglib的代理模式,翻了spring源码的话会发现,spring生成代理对象的时候使用了Objenesis来创建,Objenesis可以绕过构造方法以及相关的初始化来创建对象,所以生成的代理类中所有的属性全部都是空的。

参考文档:

 spring——事务动态代理造成属性为null_切面生成的cglb代理类里面的属性为null-CSDN博客

3、解决

1)减少直接读写属性,而是调用其中的方法

//类代码

@Service
@Slf4j
@Data
public class TestProxy {

    @Value("${test.boolean.b:true}")
    public Boolean bBoolean;

    @Async
    public void testAsync() {
    }
}

//……调用代码
    @Resource
    TestProxy testProxy;

    @GetMapping("testQuery")
    public CommonResult<Object> testQuery() {
        log.info("user func:{}", testProxy.getBBoolean());
        log.info("direct use: {}", testProxy.bBoolean);
        return new CommonResult<>();
    }

结果:使用方法获取属性则正常返回配置后的结果true,直接使用属性的为null 空

2)多自测,兄弟们,必现的case,测一下就知道了

二、Spring的代理模式

1、静态代理(Static Proxy)

1)原理

静态代理在编译时确定代理类,代理类和目标类都实现相同的接口。代理类在调用目标类的方法之前或之后添加一些额外的操作。

2)优缺点

优点

  • 简单直观:实现简单,易于理解和调试。
  • 编译时检查:代理类在编译时确定,可以进行类型检查。

缺点

  • 代码冗余:每个接口需要对应一个代理类,导致代码量增加。
  • 不灵活:代理逻辑在编译时确定,无法在运行时动态改变。
  • 维护困难:如果接口方法增加或修改,代理类需要同步修改。

3)代码实现

public interface UserService {
    void addUser(String name);
}

public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String name) {
        System.out.println("Adding user: " + name);
    }
}

public class UserServiceProxy implements UserService {
    private UserServiceImpl userService;

    public UserServiceProxy(UserServiceImpl userService) {
        this.userService = userService;
    }

    @Override
    public void addUser(String name) {
        System.out.println("Before adding user");
        userService.addUser(name);
        System.out.println("After adding user");
    }
}

// 使用代理
public class Main {
    public static void main(String[] args) {
        UserServiceImpl userService = new UserServiceImpl();
        UserServiceProxy proxy = new UserServiceProxy(userService);
        proxy.addUser("Alice");
    }
}

2、JDK动态代理(JDK Dynamic Proxy)

1)原理

JDK动态代理基于Java的反射机制,通过在运行时生成代理类来实现。代理类必须实现一个或多个接口。JDK动态代理使用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来创建代理实例。

2)优缺点

优点

  • 接口支持:适用于代理实现了接口的类。
  • 轻量级:相比于CGLIB,JDK动态代理生成的代理类较轻量。
  • 无需依赖第三方库:基于JDK自带的反射机制实现。

缺点

  • 只能代理接口:目标类必须实现接口,如果没有接口则无法使用。
  • 性能较低:反射机制相对较慢,性能不如CGLIB。

3)代码实现

public interface UserService {
    void addUser(String name);
}

public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String name) {
        System.out.println("Adding user: " + name);
    }
}

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class UserServiceProxy implements InvocationHandler {
    private Object target;

    public UserServiceProxy(Object target) {
        this.target = target;
    }

    public Object getProxy() {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method");
        Object result = method.invoke(target, args);
        System.out.println("After method");
        return result;
    }
}

// 使用代理
UserService target = new UserServiceImpl();
UserService proxy = (UserService) new UserServiceProxy(target).getProxy();
proxy.addUser("Alice");

3、cglib 代理(Code Generation Library Proxy)

1)原理

CGLIB代理通过生成目标类的子类并覆盖其方法来实现代理。它使用字节码增强技术。

2)优缺点

优点

  • 无需接口:可以代理没有实现接口的类。
  • 性能较高:通常情况下,CGLIB代理的性能比JDK动态代理高。

缺点

  • 依赖第三方库:需要CGLIB库(Spring中已经包含)。
  • 无法代理final类和方法:由于CGLIB通过生成子类来实现代理,final类和方法无法被代理。
  • 内存开销大:生成子类会占用更多内存

3)代码实现

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

public class ProductService {
    public void addProduct(String name) {
        System.out.println("Adding product: " + name);
    }
}

public class ProductServiceProxy implements MethodInterceptor {
    private Object target;

    public ProductServiceProxy(Object target) {
        this.target = target;
    }

    public Object getProxy() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Before method");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("After method");
        return result;
    }
}

// 使用代理
ProductService target = new ProductService();
ProductService proxy = (ProductService) new ProductServiceProxy(target).getProxy();
proxy.addProduct("Laptop");

总结:

  • JDK动态代理:适用于目标类实现接口的情况,轻量但性能较低,无法代理没有实现接口的类。
  • CGLIB代理:适用于没有实现接口的类,性能较高但依赖第三方库,无法代理final类和方法。
  • 静态代理:实现简单但不灵活,代码冗余,适用于代理逻辑固定的情况。

根据具体需求和应用场景,可以选择合适的代理模式。Spring默认选择JDK动态代理,如果目标类没有实现接口,则使用CGLIB代理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1855384.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解决 Pyecharts 在 jupyter Lab 上运行图片空白问题!

文章目录 &#x1f3f3;️‍&#x1f308; 1 提前声明 JUPYTER_LAB&#x1f3f3;️‍&#x1f308; 2 load_javascript(&#xff09;&#x1f3f3;️‍&#x1f308; 3 render_notebook()&#x1f3f3;️‍&#x1f308; 4 图示&#x1f3f3;️‍&#x1f308; 5 可视化项目源码…

LoRaWAN在嵌入式网络通信中的应用:打造高效远程监控系统(附代码示例)

引言 随着物联网&#xff08;IoT&#xff09;技术的发展&#xff0c;远程监控系统在各个领域的应用越来越广泛。LoRaWAN&#xff08;Long Range Wide Area Network&#xff09;作为一种低功耗广域网通信协议&#xff0c;因其长距离传输、低功耗和高可靠性等特点&#xff0c;成为…

qt开发-10_LineEdit

QLineEdit 小部件是一个单行文本编辑器。行编辑允许用户使用一组有用的编辑函数输入和 编辑一行纯文本。包括撤消和重做、剪切和粘贴以及拖放。通过更改行编辑的 echoMode()&#xff0c;它 还可以用作“只写”字段&#xff0c;用于输入如密码等. 创建好项目后&#xff0c;进入 …

407串口01发送

实验一&#xff1a; 工程。 链接&#xff1a;https://pan.baidu.com/s/1g8DV4yZWOix0BbcZ08LYDQ?pwd2176 提取码&#xff1a;2176串口1的使用。发送功能。 单片机发送信息到电脑。 通过串口进行通信。 首先单片机这边。 单片机这边&#xff0c;需要对单片机的串口模块进行使…

【面试干货】Java中的四种引用类型:强引用、软引用、弱引用和虚引用

【面试干货】Java中的四种引用类型&#xff1a;强引用、软引用、弱引用和虚引用 1、强引用&#xff08;Strong Reference&#xff09;2、软引用&#xff08;Soft Reference&#xff09;3、弱引用&#xff08;Weak Reference&#xff09;4、虚引用&#xff08;Phantom Reference…

爱心商城管理系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;管理员管理&#xff0c;企业管理&#xff0c;用户管理&#xff0c;论坛管理&#xff0c;商品管理&#xff0c;公告管理&#xff0c;用户捐赠 企业账户功能包括&#xff1a;系统首页&#xff0c;个人中…

openEuler搭建hadoop Standalone 模式

Standalone 升级软件安装常用软件关闭防火墙修改主机名和IP地址修改hosts配置文件下载jdk和hadoop并配置环境变量配置ssh免密钥登录修改配置文件初始化集群windows修改hosts文件测试 1、升级软件 yum -y update2、安装常用软件 yum -y install gcc gcc-c autoconf automake…

【ISAC】通感一体化讲座(刘凡)

高斯信道下通信感知一体化的性能极限(刘凡) 文章目录 背景背景 通信和感知在硬件结构上相似,高效地利用资源,实现相互的增益; 感知是基于不同的任务,比如目标检测(检测概率,虚警概率),估计任务(从收到的信号中去估计有用的参数,均方误差,CRB),识别(知道目标的…

Open3D 裁剪固定区域点云

目录 一、概述 二、代码实现 2.1 裁剪实现步骤 2.2完整代码 三、实现效果 3.1原始点云 3.2裁剪后点云 一、概述 在 Open3D 中&#xff0c;可以使用 SelectionPolygonVolume 来裁剪点云。SelectionPolygonVolume 允许你定义一个多边形体积&#xff0c;用于选择点云中的部…

服务治理入门

服务治理的生命周期 在微服务架构中&#xff0c;服务治理是确保服务正常运行和高效协作的关键。服务治理的生命周期包括以下五个阶段&#xff1a;服务注册、服务发现、服务续约/心跳、服务被动剔除和服务主动剔除。 服务注册 服务提供者在启动时&#xff0c;需要将其服务信…

读AI新生:破解人机共存密码笔记10人类角色

1. 工作 1.1. 技术性失业问题 1.1.1. 约翰梅纳德凯恩斯&#xff08;John Maynard Keynes&#xff09;在其著名的文章《我们后代在经济上的可能前景》中提出了技术性失业问题 1.1.1.1. 他在1930年写了这篇文章&#xff0c;当时大萧条在英国造成了大规模失业 1.1.2. 那些反对…

数据结构之二叉树的超详细讲解(3)--(二叉树的遍历和操作)

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 数据结构之二叉树的超详细讲解(3)--(二叉树的遍历和操作) 收录于专栏【数据结构初阶】 本专栏旨在分享学习数据结构学习的一点学习笔记&#xff0c;欢迎大家在评…

论文阅读--Efficient Hybrid Zoom using Camera Fusion on Mobile Phones

这是谷歌影像团队 2023 年发表在 Siggraph Asia 上的一篇文章&#xff0c;主要介绍的是利用多摄融合的思路进行变焦。 单反相机因为卓越的硬件性能&#xff0c;可以非常方便的实现光学变焦。不过目前的智能手机&#xff0c;受制于物理空间的限制&#xff0c;还不能做到像单反一…

《洛基第二季》:多元宇宙的奇幻之旅

在浩瀚无垠的影视宇宙中&#xff0c;有一部剧如璀璨星辰般耀眼&#xff0c;那就是备受期待的《洛基第二季》。该剧由贾斯汀本森和艾伦穆尔黑德执导&#xff0c;汤姆希德勒斯顿、索菲娅迪马蒂诺、欧文威尔逊等主演&#xff0c;于2023年10月上线。 这部剧延续了第一季的故事情节&…

关于 pnpm monorepo 项目中 TS2742 Error 的 workaround

最近我在使用 pnpm 作为包管理器开发一个 monorepo 项目&#xff0c;从个人体验来说&#xff0c;在 monorepo 项目中&#xff0c;pnpm 确实要比 yarn classic 用得舒心&#xff0c;最让我欣喜的是 pnpm 对 workspace 协议的支持度很好&#xff1b;另外感受比较明显的一点就是&a…

《Windows API每日一练》5.4 键盘消息和字符集

本节我们将通过实例来说明不同国家的语言、字符集和字体之间的差异&#xff0c;以及Windows系统是如何处理的。 本节必须掌握的知识点&#xff1a; 第31练&#xff1a;显示键盘消息 非英语键盘问题 字符集和字体 第32练&#xff1a;显示默认字体信息 第33练&#xff1a;创建逻…

软考高级论文真题“论大数据lambda架构”

论文真题 大数据处理架构是专门用于处理和分析巨量复杂数据集的软件架构。它通常包括数据收集、存储、处理、分析和可视化等多个层面&#xff0c;旨在从海量、多样化的数据中提取有价值的信息。Lambda架构是大数据平台里最成熟、最稳定的架构&#xff0c;它是一种将批处理和流…

静态文件及模板

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 1 静态文件 动态Web应用也会需要静态文件&#xff0c;通常是CSS和JavaScript文件。Flask可以向已经配置好的Web服务器提供静态文件&#xff0c;只要…

AI大模型企业应用实战(18)-“消灭”LLM幻觉的利器 - RAG介绍

大模型在一定程度上去改变了我们生活生工作的思考的方式&#xff0c;然后也越来越多的个人还有企业在思考如何将大模型去应用到更加实际的呃生产生活中去&#xff0c;希望大语言模型能够呃有一些更多企业级别生产落地的实践&#xff0c;然后去帮助我们解决一些业务上的问题。目…

大模型续写评测

环境准备 通过remote-ssh连接云AI服务器&#xff0c;配置文件如下&#xff0c;供参考 # Read more about SSH config files: https://linux.die.net/man/5/ssh_config Host deepseekv1HostName rootconnect.bjc1.seetacloud.comUser rootPort 52984Password xxx # 密码从申请…