• IDEA报错:Field injection is not recommended
  • 发布于 2个月前
  • 1279 热度
    0 评论
  • 顾及谁
  • 24 粉丝 32 篇博客
  •   
前言
最近项目上在做代码重构,在Bean的字段注入方式上,IDEA报了警告,Field injection is not recommended (字段注入是不被推荐的),如下图所示:

作为有代码洁癖的我不能忍,大家都是这么用的啊,为什么会有这样的警告呢?有什么替代方案呢?

为什么不推荐?
因为@Autowired在字段上注入实在是太方便了,有了@Autowired基本不需要提供setter和构造器,省去了很多代码。但是@Autowired字段注入存在下面的问题:
NPE问题
使用字段注入容易出现空指针问题,如下代码所示:因为Spring IOC容器在使用字段依赖注入时,并不会对依赖的bean是否为null做判断,因此在下面的代码中,通过 @Autowired 注入的user对象可能为空,而JVM 虚拟机在编译时也无法检测出user为null,只有在运行时调用user的方法时, 发现user为null,出现空指针异常(NPE)。
@Component 
public class FieldBasedInjection { 
    private String name; 
   // 堆代码 duidaima.com
   @Autowired 
   private final User user; 

   public FieldBasedInjection(){ 
       this.name = user.getName(); // NPE 
    } 
}
总结一下,Java 在初始化一个类时,是按照 静态变量或静态语句块 ->实例变量或初始化语句块 -> 构造方法 -> @Autowired的顺序。所以在执行这个类的构造方法时,对象实际尚未被注入,它的值还是 null, 如果属性在被注入前就拿来使用就会导致npe(空指针错误)。

和IOC容器耦合度太高
类通过属性输入,对外部不可见,类和容器的耦合度过高,导致无法脱离容器单独正确运行。比如下面的例子在Spring容器中运行没有问题。
@RestController
public class TestHandleController {

@Autowired
TestHandleService testHandleService;

public void helloTestService(){
testHandleService.hello();
}
}
如果我们用下面的方式调用呢?
TestHandleController testHandle = new TestHandleController();
testHandle.helloTestService();  // 空指针
显而易见,就会出现空指针异常,依赖对外部不可见,外界可以看到构造器和setter,但无法看到私有字段,自然无法了解所需依赖,这样十分不利于单元测试。

可能导致违反单一职责原则
使用基于字段的注解,非常简单好用无脑,我们无需关注类之间的依赖关系,完全依赖于Spring IOC容器的管理,但是使用”基于构造器注入的方式”, 我们需要手动在类代码中去编写需要依赖的类,当依赖的类越来越多,我们就能发现 code smell,这个时候就能显示的提醒我们,代码的质量是否有问题。因此,尽管字段注入不直接负责打破单一责任原则,但它通过隐藏了和构造器注入一样发现code smell的机会。示例代码:
@Component 
public class ConstructorBasedInjection {
    private final Object object; 
    private final Object object2;         
             ... 
    private final Object objectX;  
     // 堆代码 duidaima.com
    @Autowired 
    public ConstructorBasedInjection(Object object, 
                                                  Object object2, 
                                                    ...       ,
                                                  Object objectX) {    
                 this.object = object;     
                 this.object2 = object2; 
                       ...     
                 this.objectX = objectX; 
        }
}
和Spring框架高度耦合
@Autowired是Spring框架中的注解,如果你的应用程序想要更换一个IOC框架,虽然这种情况非常非常低,这时候你就需要修改大量的代码了。更推荐的是使用 @Resource注解,@Resource注解是JSR-250提供的,它是Java标准,我们使用的IOC容器应当去兼容它,这样即使更换容器,也可以正常工作。

上面我们分析了基于@Autowired字段注入方式的各种问题,所以IDEA也不推荐,那么如何解决呢?

更推荐的做法
【强烈推荐】使用构造器方式注入

这也是Spring官方强烈推荐使用基于构造器注入的方式, 像国内Dubbo、RocketMQ等很多开源框架的源码都已经转向了基于构造器的注入方式,所以开发中我们应该尊重Spring官方的推荐,尽管其他的方式可以解决,但是不推荐。


【一般推荐】使用@Resource注解
如果你不喜欢构造器注入的方式,觉得使用构造器注入的方式麻烦,还要写代码,虽然不建议你这么想。那么更推荐你使用@Resource注解,@Resource是JSR-250提供的,不是Spring中的注解,它是Java标准,我们使用的IoC容器应当去兼容它,这样即使更换容器,也可以正常工作。如果你使用这个注解IDEA也不会提示警告。

我们再来看看这两者的区别, @AutowiredVS@Resource。
提供方
. @Autowired是由Spring提供的,包名是:org.springframework.beans.factory.annotation
. @Resource 是由Java提供的,包名是:javax.annotation
依赖识别方式
. @Autowired默认是以byType方式,可以使用@Qualifier指定bean名称,如果找不到Bean不会自动使用byName方式。
. @Resource 默认是以byName方式,当byName方式无法匹配时,会使用byType方式。(仅适用于仅注册了一个Bean对象的类型)
适用对象
. @Autowired 可以使用在方法,方法参数,构造器,构造器参数,字段上
.@Resource只能使用在方法,字段上(经过实测,无法注解在构造器和参数上)
强依赖型

. @Autowired和@Resource都是具有强依赖性,也就是必须要有这个bean才能启动,不过@Autowired可以设置属性required=false变成非强制注入。


【不大推荐】关闭警告提示
如果你是一个非常懒的人,不想改动任何代码,但又想去掉提示的话,那么我建议你可以直接关掉警告提示。
打开Editor–>Inspections–>Spring->Spring Core->Code–>Non recommended ‘field’ injections,去掉右边的小勾勾,Apply–>OK即可。

但是你换一台电脑,重置配置后就又出现了。

总结
本文讲解了Spring字段注入时IDEA提示的警告信息,讲解这种注入方式的缺点,希望大家在今后的开发过程中多多使用构造器注入的方式,养成良好的编码习惯。
用户评论