Spring 依赖注入最佳实践? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
tinotino654321
V2EX    Java

Spring 依赖注入最佳实践?

  •  
  •   tinotino654321 2022-02-21 19:56:52 +08:00 5896 次点击
    这是一个创建于 1329 天前的主题,其中的信息可能已经有所发展或是发生改变。
    Spring 官方文档里推荐的是用 Constructor 注入,可选依赖用 Setter 注入。
    但是实际在项目里一般用哪种注入呢?
    我工作没多久,见到的大部分都是用 Field 注入。
    50 条回复    2022-02-23 10:52:08 +08:00
    chendy
        1
    chendy  
       2022-02-21 20:04:28 +08:00
    final 字段 + 构造方法注入 + lombok 的 RequireArgsConstructor 注解
    是不是最佳不知道,应该是字最少的
    但是不能用来处理 RestTemplateBuilder 这样的特殊 bean
    issakchill
        2
    issakchill  
       2022-02-21 20:13:19 +08:00
    我也是用 1 楼的方法
    有个难处理的地方是这个类的子类要手写构造方法,不知道有没有更方便的做法?
    sutra
        3
    sutra  
       2022-02-21 20:24:56 +08:00
    Constractor 注入的话,可以把 field 标记为 final ,其它的则不行。
    sutra
        4
    sutra  
       2022-02-21 20:30:19 +08:00   1
    fpure
        5
    fpure  
       2022-02-21 20:41:41 +08:00   16
    我不管,我就要用 @Autowire 属性注入
    Bingchunmoli
        6
    Bingchunmoli  
       2022-02-21 20:52:04 +08:00
    官方推荐使用构造器,而旧项目和旧 Spring 用的比较多的是 autowired 和 resource , 都行,其实按推荐确实好点
    7911364440
        7
    7911364440  
       2022-02-21 20:54:11 +08:00
    Constractor+1
    Oktfolio
        8
    Oktfolio  
       2022-02-21 20:55:07 +08:00
    我是 Constractor + Setter
    Oktfolio
        9
    Oktfolio  
       2022-02-21 20:55:26 +08:00
    @Oktfolio 啊这,复制了楼上的 typo
    9c04C5dO01Sw5DNL
        10
    9c04C5dO01Sw5DNL  
       2022-02-21 21:19:24 +08:00
    如果依赖是创建对象时必须的,肯定是构造器注入。否则应该用 setter 注入,最次是 field 。

    以上是基于:1. 依赖是否必须在构造对象时提供,2. 方便 mock

    但实际使用使用是另一回事。用 spring 那一套,即便用 filed 注入一般也没什么问题
    tinotino654321
        11
    tinotino654321  
    OP
       2022-02-21 21:35:26 +08:00
    @sutra 实际上很少有依赖注入后会被修改的情况吧? final 其实也不是很重要?
    sutra
        12
    sutra  
       2022-02-21 21:41:30 +08:00
    @tinotino654321 final 的目的是为了防止被修改,也可以从语义角度表示,不可以修改。
    sutra
        13
    sutra  
       2022-02-21 21:42:09 +08:00
    @Oktfolio 我发完就发现我的 typo 了,然而 V2EX 不能改。哈哈哈……
    tinotino654321
        14
    tinotino654321  
    OP
       2022-02-21 21:42:12 +08:00
    @giiiiiithub 我觉得 Field 注入最方便,但既是优点也是缺点。
    优点是可以循环依赖,而且加依赖方便,不用改 Constructor 。
    缺点是 Class 容易循环依赖,责任不清晰,并且依赖很多,容易形成屎山。
    Mock 的话 Mockito 也可以 InjectMocks ,问题不是很大。
    sutra
        15
    sutra  
       2022-02-21 21:48:01 +08:00 via iPhone
    constructor 注入也能循环依赖,加 lazy 注解。
    9c04C5dO01Sw5DNL
        16
    9c04C5dO01Sw5DNL  
       2022-02-21 22:02:08 +08:00
    @tinotino654321 跟依赖循环没多大关系。主要是依赖应该不应该放在构造函数?能不能被 mock ? 你可以想象如果提供给外部使用,就需要问自己两个问题:1. 什么是必须的什么是可选的? 2. 什么是可以被修改的什么是不能被修改的?
    liuxu
        17
    liuxu  
       2022-02-21 22:15:08 +08:00
    新版的 idea 不是推荐 contractor 里面用 @autowired 注入么
    WispZhan
        18
    WispZhan  
       2022-02-21 22:59:42 +08:00 via Android
    必然是按官方来,不然提 PR 都过不了 bot
    honamx
        19
    honamx  
       2022-02-21 23:23:06 +08:00
    @RequiredArgsConstructor + private final
    appstore54321
        20
    appstore54321  
       2022-02-21 23:35:41 +08:00 via Android
    构造函数注入,写单元测试时就知道了,这样最方便 mock 。直接 autowired 一个成员这怎么测。
    NotFoundEgg
        21
    NotFoundEgg  
       2022-02-21 23:51:35 +08:00
    我一般用构造器注入
    用 Autowired 注入 idea 会有警告,看着难受
    leeg810312
        22
    leeg810312  
       2022-02-21 23:52:51 +08:00 via Android
    顺便问下,service 接口实现按推荐的构造函数注入依赖,那么单元测试 mockito 怎么写比较好
    Kontinue
        23
    Kontinue  
       2022-02-22 08:25:37 +08:00
    @NotFoundEgg 同,idea 推荐哪种方式用哪种哈哈哈,不然全是波浪线和 warning 很难受。人家既然给出提示肯定是有他的必要性的
    thetbw
        24
    thetbw  
       2022-02-22 09:11:17 +08:00
    @appstore54321 我们从没写过单元测试
    chendy
        25
    chendy  
       2022-02-22 09:19:41 +08:00
    @leeg810312 直接 @ Mock 定义 mock 依赖,然后自己把 service new 出来
    hingbong
        26
    hingbong  
       2022-02-22 09:24:28 +08:00
    kotlin 表示一般用 constructor, 偶尔是 setter
    gitdoit
        27
    gitdoit  
       2022-02-22 09:25:33 +08:00   2
    idea 治好了我用 @Autowired 的毛病
    sheeta
        28
    sheeta  
       2022-02-22 09:26:15 +08:00
    @Kontinue @Resource 注入就没有警告了,哈哈
    summerLast
        29
    summerLast  
       2022-02-22 09:56:18 +08:00
    同一楼 final 字段 + 构造方法注入 + lombok 的 RequireArgsConstructor 注解
    补 需要 注入值的 或 特殊的 不用 final 修饰 自己手动加对应注解
    MarkLazy
        30
    MarkLazy  
       2022-02-22 09:57:08 +08:00
    @sutra 这篇文章讲了 constructor 注入可以防止空指针,但是我试了 `@AutoWired/@Resource` 注解,用这两个注解时,缺依赖的话 springboot 都启动不起来,所以不管是构造器注入还是字段注入都不会造成空指针啊
    wolfie
        31
    wolfie  
       2022-02-22 09:58:41 +08:00
    @qaqLjj #30
    构造器注入,没有合格的 bean 一样没法创建。
    hambers
        32
    hambers  
       2022-02-22 10:14:25 +08:00
    构造器注入 如果以来的注入对象很多的话,构造方法不是参数很多的样子。 强迫症看着有点不舒服,有啥好办法么
    Edsie
        33
    Edsie  
       2022-02-22 10:29:11 +08:00
    推荐构造器注入,但实际用的时候常用 @AutoWired
    banmuyutian
        34
    banmuyutian  
       2022-02-22 11:40:04 +08:00
    @hambers #32
    lombok 注解
    xuanbg
        35
    xuanbg  
       2022-02-22 11:47:33 +08:00
    field 注入最简单,但安全性不太好。我都是用构造器注入,这样不会飘红,强迫症你们不用学。
    xuanbg
        36
    xuanbg  
       2022-02-22 11:48:39 +08:00
    @hambers 注入对象很多的话,你要反思自己的代码结构是否合理了。
    NotFoundEgg
        37
    NotFoundEgg  
       2022-02-22 11:54:44 +08:00
    @hambers
    Lombok 的 RequireArgsConstructor 注解可以解决这个问题,但从设计上来说一个类注入了过多的对象,可能违背了单一职责原则(虽然实际上来说这种情况确实特别常见)
    wolfie
        38
    wolfie  
       2022-02-22 12:02:37 +08:00
    @appstore54321 #20
    单元测试时候 脱离 spring ?
    dcsuibian
        39
    dcsuibian  
       2022-02-22 12:04:38 +08:00
    Constractor ,因为 idea 不会有波浪线。(测试的那里不会有)
    循环依赖用 settter
    chendy
        40
    chendy  
       2022-02-22 12:15:24 +08:00
    @wolfie 依赖全 mock ,直接脱离 spring 单独跑
    spring 的测试工具其实更多是集成测试不是单元测试(带数据库和接口之类的)
    cheng6563
        41
    cheng6563  
       2022-02-22 14:19:36 +08:00
    构造器注入的话 如果循环依赖会炸掉
    shanghai1943
        42
    shanghai1943  
       2022-02-22 14:21:00 +08:00
    构造器注入的话,万一是需要注入很多 bean 咋整。。不得写一长串么
    zzfer
        43
    zzfer  
       2022-02-22 16:41:21 +08:00   1
    @shanghai1943 我们使用的 lombok 的 RequireArgsConstructor 注解
    shanghai1943
        44
    shanghai1943  
       2022-02-22 16:46:24 +08:00
    @zzfer 学习了。
    incubus
        45
    incubus  
       2022-02-22 17:23:23 +08:00
    @fpure 为啥不用 @Resource
    zhady009
        46
    zhady009  
       2022-02-22 17:28:41 +08:00
    用构造器就行了 写多了就明白为什么 也能一定程度上把控代码质量
    hambers
        47
    hambers  
       2022-02-22 17:52:55 +08:00
    @banmuyutian 如果 有几个待注入的 field 有循环以来需要 lazy 一下 咋整呢? 能在 final field 上加 @lazy
    Loku
        48
    Loku  
       2022-02-22 18:04:27 +08:00
    Constructor 注入
    NeoZephyr
        49
    NeoZephyr  
       2022-02-23 10:44:18 +08:00
    不知道为啥不推荐 field 注入。如果全部用构造器注入的话,会遇到循环依赖的
    deweixu
        50
    deweixu  
       2022-02-23 10:52:08 +08:00
    我以前用 @AutoWired ,但是 idea 会给我警告,现在改用 setter 了
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2689 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 14:50 PVG 22:50 LAX 07:50 JFK 10:50
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86