当前位置: 首页 > news >正文

Spring 依赖注入的三种方式,踩过坑之后我才知道该用哪个

学 Spring 的时候,依赖注入这个概念本身不算难,但是注入方式有三种,教程里各写各的,看完之后我反而更纠结了:到底该用哪个?

后来在项目中三种都试过一遍,也踩了一些坑,总算搞清楚了它们各自适合什么场景。这里记录一下。

先回顾一下依赖注入是什么

用一句话说就是:你不用自己new对象了,Spring 容器帮你把需要的东西送过来。

举个例子,UserService需要用到UserRepository来查数据库。传统写法是在类里面直接new一个出来,但这样 UserService 就和某个具体的 Repository 实现绑死了。依赖注入的做法是,你只声明"我需要一个 UserRepository",Spring 容器会在运行时把合适的实现塞给你。

那问题就来了:Spring 是怎么把东西塞给你的?这就有了三种方式。

第一种:构造器注入

在构造函数里接收依赖。

@ServicepublicclassUserService{privatefinalUserRepositoryuserRepository;publicUserService(UserRepositoryuserRepository){this.userRepository=userRepository;}}

这是 Spring 官方推荐的方式。我一开始不理解为什么推荐它,觉得写起来还挺啰嗦的,每个依赖都要写一遍构造函数参数。后来踩了几个坑才明白它好在哪里。

第一个好处是,对象在创建出来的那一刻,所有必需的依赖就已经到位了。不会出现用到一半发现某个依赖是 null 的情况。这个听起来理所当然,但其他方式真的不保证这一点。

第二个好处是依赖字段可以声明为finalfinal意味着这个引用一旦赋值就不会再变,在多线程环境下天然安全,不用担心哪个地方偷偷把它改了。

第三个好处是做单元测试的时候特别方便。脱离了 Spring 容器,你直接new UserService(mockRepository)就能测试,不需要启动任何 Spring 的东西。这个在写测试的时候体感很明显。

还有一个小细节:Spring 4.3 之后,如果一个类只有一个构造函数,@Autowired注解可以省略。所以写起来也没那么啰嗦了。

第二种:Setter 注入

容器先把对象创建出来,然后调用 Setter 方法把依赖传进去。

publicclassPaymentService{privatePaymentGatewaygateway;@AutowiredpublicvoidsetGateway(PaymentGatewaygateway){this.gateway=gateway;}}

这种方式的好处是灵活。对象创建之后,你还可以在运行过程中重新调用 Setter 换一个依赖实现进去。

但问题也很明显:对象刚创建出来的时候,依赖可能还没注入。如果在这期间有人调了业务方法,gateway还是 null,直接就空指针了。

我在一个小项目里用过 Setter 注入,当时觉得代码看着还行。后来有次改代码,忘了配置某个 Bean 的注入,跑起来之后隔了很久才报 NullPointerException,排查半天才发现是 Setter 没被调到。如果是构造器注入,启动的时候就会报错,不用等到运行时才炸。

所以 Setter 注入比较适合那种"有没有都行"的依赖。比如某个服务有一个可选的通知功能,不配就用默认的,配了就覆盖。这种场景下 Setter 注入的灵活性就有用了。

第三种:字段注入

直接在字段上加@Autowired,不写构造函数也不写 Setter。

@ServicepublicclassOrderService{@AutowiredprivateOrderRepositoryorderRepository;}

代码最少,一眼看过去特别干净。我刚开始学 Spring 的时候最喜欢这种写法,因为省事。

但用了一段时间之后,问题就出来了。

首先是依赖被藏起来了。你从类的构造函数和公开方法上完全看不出这个类需要什么才能运行。新接手的人看代码,以为OrderService很简单,拿来用才发现背后还藏着一个OrderRepository,得先把这个也配好才行。

其次是没法声明final。因为@Autowired字段注入是 Spring 通过反射在对象创建之后才赋值的,final字段不允许这样操作。

最让我头疼的是写测试的时候。你想给orderRepository传一个 Mock 对象进去,但它是 private 字段,也没有 Setter。要么启动 Spring 测试容器,要么用反射工具强行塞进去。一个简单的单元测试搞得特别复杂。

后来我翻了一些开源项目的代码,发现成熟的代码库几乎不用字段注入。Spring 官方也不推荐在生产代码里大量使用。

所以到底该怎么选

踩完坑之后,我自己的规则是这样的:

大部分情况下用构造器注入。代码虽然多写几行,但依赖关系清清楚楚,启动时就能检查完所有依赖,写测试也最方便。

Setter 注入留给可选依赖。就是那种"没有也能跑,有了更好"的场景。

字段注入能不用就不用。它写起来确实最省事,但省下来的那几行代码,后面排查问题和写测试的时候都得还回去。

最后放一张总结表,方便以后回来翻:

方式代码量依赖安全final测试友好推荐场景
构造器注入稍多保证可以核心业务,绝大多数场景
Setter 注入中等不保证不行一般可选依赖,有默认值
字段注入最少不保证不行尽量别用
http://www.jsqmd.com/news/1039721/

相关文章:

  • 飞思卡尔MSC8101 DSP中断控制器原理与配置实战指南
  • 凯乐石携手小沓AI:加速品牌数字化转型,迈向AI驱动新未来
  • Sketch Find and Replace插件:设计师的批量文本替换终极解决方案
  • Microchip 24XX256 I2C EEPROM选型、电路设计与软件驱动全解析
  • Digital-IDE:3步在VSCode中搭建专业硬件开发环境
  • 研发效能与合规并重:ALM工具在强监管行业中的落地实践
  • 炉石传说终极插件指南:如何用HsMod快速提升游戏体验
  • 通信受限下的量化在线LQR控制:原理、算法与信息论极限
  • ATM通信中缓冲区描述符与连接表:DMA驱动网络接口的核心机制
  • 总线分析器原理与实战:嵌入式调试中的逻辑时序洞察利器
  • 嵌入式开发外设访问与代码优化:从寄存器操作到组件化实践
  • 如何在10分钟内为《原神》安装自定义模型导入工具:终极快速指南
  • 宣总管:软文发布网站如何助力企业获得AI时代结构性红利?
  • 如何免费解锁Cursor Pro功能:3步实现AI编程助手无限使用终极指南
  • OCAuxiliaryTools:3分钟掌握黑苹果OpenCore配置的终极指南
  • 驱动调试:从内核崩溃到设备稳定的系统化排障方法论
  • Digital-IDE终极指南:在VSCode中构建专业硬件开发环境
  • 告别手动刷新!3分钟搭建B站内容自动化监控系统
  • ComfyUI_smZNodes:跨平台AI图像生成一致性终极指南
  • [智能体-450]:单 Agent(自主规划模式),如何大模型更精确的决策,调用外部插件和内部记忆单元?
  • Cursor Pro账户管理终极指南:如何轻松绕过设备限制实现多账户自由切换
  • Windows 下利用QT编译boost_1_53_0
  • 基于Django框架的门窗定制管理系统的设计与实现
  • AI 赋能电商产业增长新生态,梦饷科技入选上海市 AI 助力商业领域品牌发展案例集
  • 如何3步解决Cursor账户限制:开源工具的终极使用指南
  • 猫抓浏览器扩展:网页视频资源一键获取神器
  • 华为AI沉默之谜:表面低调,实则下着一盘改变格局的超级大棋
  • 深度学习模型剪枝与部署实战:从YOLOv8到Android端实时推理
  • YOLOv5模型昇腾部署全链路:从ONNX到ATC编译与.om推理
  • 2026年沈阳铁西区养车服务商深度解析:一站式专业养护如何选择 - 品牌鉴赏官2026