@Autowired 注入转换为构造器注入,导致的构造器代码臃肿,除了 Lombok 外还有别的解决方案吗?

2020-10-10 11:30:45 +08:00
 vate32
各博客文章说的都是要么使用 Lombok 的 @ RequiredArgsConstructor 注解,要么“考虑这个类是符合足单一职责原则了,将这个类拆分为多个类”。可是对于前者,项目不一定使用 Lombok,对于后者,实际业务情况可能使用很多个依赖类,拆分不太现实。请问还有别的优雅的解决方案吗?🤔
5635 次点击
所在节点    Java
28 条回复
wallfacers
2020-10-10 17:29:35 +08:00
@lychs1998 构造器注入不是 @Autowired,两者实现区别很大。 @Autowired 是通过 AutowiredAnnotationBeanPostProcessor 进行注入的,而构造器注入是在 Bean 的创建阶段,就会去找到最匹配的构造器,并且准备好构造器需要依赖的 Bean
ic2y
2020-10-10 17:43:31 +08:00
@vate32 Autowired 注入在多人大型项目里,我碰到过一些问题,因为团队成员的理解不一样:容易造成滥用(可能是为了复用某个能力,就注入一堆 bean )、循环依赖(底层模型注入了高层模型的 bean,可能交叉)、多模块的项目很难管理(有种拔出萝卜带出泥的感觉,因为 A 和 B 项目共享了模块 C,B 项目在 C 模块增加了一个 Autowired 注入,A 项目没有感知)。

我自己认为,构造器注入更好,Spring 官方也推荐,构造器注入在一定程度上:强迫开发者去思考,当前的 bean 为什么要注入其他的依赖。

如果你用了 Lombok 自动实现了构造器注入,那么这种强迫开发者去思考的动机就没有了,还不如直接 Autowired 。

手动的用构造器注入(除了某些特定的类,例如 Controller 之类的),我认为有如下优点:
1.强迫我自己思考:这个注入是否是必须的,是否可以用工具类?用设计模式?或者其他的优雅方法来实现这个逻辑设计。
2.注入的改变是可感知的。在大型多模块架构的协作工程里,可能很有用。大家在协作里复用了模块 C,一旦你在模块里增加了注入参数,那么其他人用到了模块 C 注入 bean,会立刻编译错误。
3.因为大型工程里,一般会把某个模块作为一个 jar 包,让其他应用引用,Configuration 里动态声明 Bean 的时候,感觉更从容。因为我知道当前的 bean 所有的依赖都是构造方式注入,我可以从容选择一组自己指定的 bean 进去组装。
4.某种意义上,实现了业务代码和 Spring 框架的解耦。因为构造器注入的话,就像在写普通的 java 的 pojo 。我们不需要 @Autowired 不需要 @Service 和 @Component,业务类里基本不会 import spring 的东西,依赖 Configuration 的动态装配能力,从构造注入声明了一个个的 bean,完全可控(适合有洁癖的人)。如果有场景,需要移植的话,会很容易,例如:从 Spring 框架移植到 google 的 guice 框架。

那么,如果构造器注入的 bean 很多的时候,也是一个强迫性的提醒:是实时思考当前的 bean 的设计问题了。
wysnylc
2020-10-10 18:32:55 +08:00
构造器无法解决循环依赖问题,spring 推荐构造器注入同时也说了该问题
没能力解决这个问题的就别瞎折腾
EminemW
2020-10-10 22:48:55 +08:00
不可以实现 InitializingBean 接口嘛
br00k
2020-10-11 01:54:54 +08:00
推荐构造方法注入。IDE 可以根据属性自动生成构造方法。
因为构造方法注入默认是不能循环引用,循环引用更多是代码结构或设计不合理,这样能约束你的代码,逼迫你去思考避免循环引用,这是非常好的。
至于说非要循环引用,请在构造参数上使用 @Lazy 就可以了。这个是不建议的,如果都这样写,还不如用 autowired 。
我的习惯是只有在 Bean 自己注入自己才会用 @Lazy,这样是避免使用 this 调用内部方法 AOP 失效。
1194129822
2020-10-11 09:33:35 +08:00
现在大多数项目还是 Filed 注入,spring3 推荐 setter 注入,4 推荐构造器注入,且 4.3 后如果 bean 中有构造器,则不需要 @Autowired, 就已经使用构造器注入,复杂的是 spring 构造器,setter,Filed 注入可以一起工作,所以你使用 Lombok,只要用了 AllArgsContructor/RequiredArgsConstructor,就不需要其他 spring 注解。而且构造器注入无法解决循环依赖,而且是生成的构造器,你也无法操作,所以 @Lazy 解决循环依赖不可用. 但是依然可以在需要循环依赖的 Bean 上使用 @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)提前暴露代理 Bean,不过 spring 官方推荐构造器注入的原因就是程序员自己解决循环依赖
sheng3233386
2020-10-12 16:44:13 +08:00
@vate32 避免不了空指针,在构造函数的调用逻辑建议在 PostConstruct 注解的方法里执行。Resource 没有你说的 warning,哈哈
Xhack
2023-01-14 12:21:39 +08:00
@ic2y 需要注入的类 比如 xxService 也不需要 @Service 注解吗 ???????

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/713586

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX