Spring依赖注入三种方式的思考和理解

Spring依赖注入三种方式的思考和理解

前言

Spring框架的依赖注入特性非常灵活好用,并且提供了三种注入方式—Field注入、Constructor注入、Setter方法注入,但是目前各论坛有大量关于注入方式优缺点的讨论,这里只是记录我个人对于注入方式的思考及理解。


先放出个人对注入方式的思考和理解

  1. 尽量避免Field注入;
  2. 必要且不应被修改的依赖使用Constructor注入。配合Spring4.3及Lombok插件,代码会变得非常简洁,推荐大量使用
  3. Setter注入仅用于静态变量的注入、解决依赖循环、需要灵活调整的依赖(非常少见)。同时需要注意安全性,应在Setter方法中验证调用栈的合法性;

三种注入方式对比

  • Field注入

    @Autowired
    private SysUserService userService;
    
    • ​ 优点

      • 增加修改都比较灵活;
      • 注入简单,只需在字段上加@Autowired或@Resource;
      • 美观,无需冗余代码
    • 缺点

      • 容易出现空指针异常,Field 注入允许构建对象实例的时候依赖的示例对象为空,这就导致了空指针异常无法尽早的暴露出来;

      • 依赖不能被final修饰,可能在调用过程中被修改;

      • 与DI高度耦合,脱离Spring容器后无法使用,不符合JavaBean的规范;

      • 无法利用到Spring启动时的三级缓存,容易产生依赖循环;

      • 对单元测试不友好,类必须通过反射实例化,单元测试时需要等待Spring实例化所有被管理的Bean;

  • Constructor注入

    @Component // 任何高级@Component注解均可,比如@Service、@Controller、@Configuration
    public class SysUserController {
        private SysUserService userService;
      
        @Autowired // Spring4.3及更新的版本中,如果我们的类中只有单个构造函数,那么Spring就会实现一个隐式的自动注入,可以不写此注解。
        public HelloBean(SysUserService userService) {
            this.userService = userService;
        }
    }
    
    • 优点

      • 可以利用到Spring的三级缓存,可以解决部分依赖循环;
      • 将空指针等问题的暴露提前至编译阶段,更早暴露问题。
      • 构造器生成的是初始化完全的对象;
      • 依赖可以被final修饰;
      • 可以明确指出依赖关系;
      • 与DI解耦;
      • 单元测试友好,只需创建相关依赖即可;
    • 缺点

      • 代码冗余,维护繁琐;

      • 大部分依赖不会同时用到,相比Setter注入方式不够灵活;

      • CGLIB不能创建代理,迫使你使用基于接口的代理或虚拟的无参数构造函数;

  • Setter注入

    private SysUserService userService;
    @Autowired
    public void setDependencyB(SysUserService userService) {
        this.userService = userService;
    }
    
    • 优点
      • 完全免疫依赖循环;
      • 灵活,可以随时改变依赖;
      • 只有当对象是需要被注入的时候它才会帮助我们注入依赖,而不是在初始化的时候就注入;
      • 将空指针等问题的暴露提前至编译阶段,更早暴露问题。
      • 与DI解耦;
    • 缺点
      • 依赖不能被final修饰,可能在调用过程中被修改;
      • 代码冗余,维护繁琐;

Lombok + Constructor注入(Spring4.3及更新版本)

在Spring 4.3 以后,如果我们的类中只有单个构造函数,那么Spring就会实现一个隐式的自动注入,此时加上Lombok插件的@RequiredArgsConstructor注解1,代码会变得非常简洁

@Component // 任何高级@Component注解均可,比如@Service、@Controller、@Configuration
@RequiredArgsConstructor
public class SysUserController {
    private final SysUserService userService;
}

Spring官方文档对于注入方式的推荐

在Spring 3.x 中,Spring团队建议使用setter来注入:

872578-20180124221607490-995759185

有大量参数时,Constructor会很笨拙,特别当属性不必须时。而使用Setter方法可以灵活的调用以改变依赖。

一些纯粹主义者(???)更喜欢Constructor注入,Constructor提供了所有的依赖,这意味着构造器生成的是初始化完全的对象,缺点是对象变得难以重新配置和改变依赖。

而在Spring4.x中,Spring团队建议使用Constructor注入:

872578-20180124222421209-1494071579

Constructor注入允许成员变量被final修饰,并确保所依赖项不为空。此外,构造器生成的是初始化完全的对象。并且在Constructor中有大量参数是很糟糕的代码习惯,会体现出这个类有太多职责,不符合Java单一职责原则,应该被重构。

Setter注入应且只应用于可设置合理默认值且不必须的依赖(浓浓的翻译腔),另外,非空检查必须作用在所有依赖上。Setter注入的一个好处是可以灵活的调用以改变依赖。


结语

码字不易,如有其他看法,欢迎留言或私信讨论。如果解决了你的一些困惑或对你有所帮助,点个赞再走吧。


  1. 1:@RequiredArgsConstructor注解修饰的类在编译时会生成一个带有必要参数的构造器,必要参数为带有@NotNull注解的字段、被final修饰的字段 ↩︎

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值