淘宝网站建设退款,公司网站设计报价,网页游戏2022排行榜前十名,网站服务器租用怎样收费来源 | juejin.cn/post/6844904056230690824作者 | Richard_Yi本文的内容主要是想探讨我们在进行 Spring 开发过程当中#xff0c;关于依赖注入的几个知识点#xff0c;具体内容如下#xff1a;Autowired, Resource, Inject 三个注解的区别当你在使用Autowired时#xff0… 来源 | juejin.cn/post/6844904056230690824作者 | Richard_Yi本文的内容主要是想探讨我们在进行 Spring 开发过程当中关于依赖注入的几个知识点具体内容如下Autowired, Resource, Inject 三个注解的区别当你在使用Autowired时是否有出现过Field injection is not recommended的警告你知道这是为什么吗Spring 依赖注入有哪几种方式官方是怎么建议使用的呢如果你对上述问题都了解那我个人觉得你的开发经验应该是不错的。下面我们就依次对上述问题进行解答并且总结知识点。Autowired, Resource, Inject 三个注解的区别Spring 支持使用Autowired, Resource, Inject 三个注解进行依赖注入。下面来介绍一下这三个注解有什么区别。AutowiredAutowired为Spring 框架提供的注解需要导入包org.springframework.beans.factory.annotation.Autowired。这里先给出一个示例代码方便讲解说明public interface Svc {void sayHello();
}Service
public class SvcA implements Svc {Overridepublic void sayHello() {System.out.println(hello, this is service A);}}Service
public class SvcB implements Svc {Overridepublic void sayHello() {System.out.println(hello, this is service B);}}Service
public class SvcC implements Svc {Overridepublic void sayHello() {System.out.println(hello, this is service C);}
}测试类SpringBootTest
public class SimpleTest {Autowired// Qualifier(svcA)Svc svc;Testvoid rc() {Assertions.assertNotNull(svc);svc.sayHello();}}
装配顺序按照type在上下文中查找匹配的bean查找type为Svc的bean如果有多个bean则按照name进行匹配如果有Qualifier注解则按照Qualifier指定的name进行匹配查找name为svcA的bean如果没有则按照变量名进行匹配查找name为svc的bean匹配不到则报错。Autowired(requiredfalse)如果设置required为false(默认为true)则注入失败时不会抛出异常Inject在Spring 的环境下Inject和Autowired 是相同的因为它们的依赖注入都是使用AutowiredAnnotationBeanPostProcessor来处理的。Inject是 JSR-330 定义的规范如果使用这种方式切换到Guice也是可以的。Guice 是 google 开源的轻量级 DI 框架如果硬要说两个的区别首先Inject是Java EE包里的在SE环境需要单独引入。另一个区别在于Autowired可以设置requiredfalse而Inject并没有这个属性。ResourceResource是JSR-250定义的注解。Spring 在 CommonAnnotationBeanPostProcessor实现了对JSR-250的注解的处理其中就包括Resource。Resource有两个重要的属性name和type而Spring 将Resource注解的name属性解析为bean的名字而type属性则解析为bean的类型。装配顺序如果同时指定了name和type则从Spring上下文中找到唯一匹配的bean进行装配找不到则抛出异常。如果指定了name则从上下文中查找名称id匹配的bean进行装配找不到则抛出异常。如果指定了type则从上下文中找到类型匹配的唯一bean进行装配找不到或是找到多个都会抛出异常。如果既没有指定name又没有指定type则默认按照byName方式进行装配如果没有匹配按照byType进行装配。IDEA 提示 Field injection is not recommended在使用IDEA 进行Spring 开发的时候当你在字段上面使用Autowired注解的时候你会发现IDEA 会有警告提示Field injection is not recommendedInspection info: Spring Team Recommends: Always use constructor based dependency injection in your beans. Always use assertions for mandatory dependencies.翻译过来就是这个意思不建议使用基于 field 的注入方式。Spring 开发团队建议在你的Spring Bean 永远使用基于constructor 的方式进行依赖注入。对于必须的依赖永远使用断言来确认。比如如下代码Service
public class HelpService {AutowiredQualifier(svcB)private Svc svc;public void sayHello() {svc.sayHello();}
}public interface Svc {void sayHello();
}Service
public class SvcB implements Svc {Overridepublic void sayHello() {System.out.println(hello, this is service B);}
}
将光标放到Autowired处使用Alt Enter 快捷进行修改之后代码就会变成基于Constructor的注入方式修改之后Service
public class HelpService {private final Svc svc;Autowiredpublic HelpService(Qualifier(svcB) Svc svc) {// Assert.notNull(svc, svc must not be null);this.svc svc;}public void sayHello() {svc.sayHello();}
}
如果按照Spring 团队的建议如果svc是必须的依赖应该使用Assert.notNull(svc, svc must not be null)来确认。修正这个警告提示固然简单但是我觉得更重要是去理解为什么Spring 团队会提出这样的建议直接使用这种基于 field 的注入方式有什么问题首先我们需要知道Spring 中有这么3种依赖注入的方式基于 field 注入属性注入基于 setter 注入基于 constructor 注入构造器注入1. 基于 field 注入所谓基于 field 注入就是在bean的变量上使用注解进行依赖注入。本质上是通过反射的方式直接注入到field。这是我平常开发中看的最多也是最熟悉的一种方式同时也正是 Spring 团队所不推荐的方式。比如Autowired
private Svc svc;
2. 基于 setter 方法注入通过对应变量的setXXX()方法以及在方法上面使用注解来完成依赖注入。比如private Helper helper;Autowired
public void setHelper(Helper helper) {this.helper helper;
}
注在 Spring 4.3 及以后的版本中setter 上面的 Autowired 注解是可以不写的。3. 基于 constructor 注入将各个必需的依赖全部放在带有注解构造方法的参数中并在构造方法中完成对应变量的初始化这种方式就是基于构造方法的注入。比如private final Svc svc;Autowired
public HelpService(Qualifier(svcB) Svc svc) {this.svc svc;
}
在 Spring 4.3 及以后的版本中如果这个类只有一个构造方法那么这个构造方法上面也可以不写 Autowired 注解。基于 field 注入的好处正如你所见这种方式非常的简洁代码看起来很简单通俗易懂。你的类可以专注于业务而不被依赖注入所污染。你只需要把Autowired扔到变量之上就好了不需要特殊的构造器或者set方法依赖注入容器会提供你所需的依赖。基于 field 注入的坏处成也萧何败也萧何基于 field 注入虽然简单但是却会引发很多的问题。这些问题在我平常开发阅读项目代码的时候就经常遇见。容易违背了单一职责原则 使用这种基于 field 注入的方式添加依赖是很简单的就算你的类中有十几个依赖你可能都觉得没有什么问题普通的开发者很可能会无意识地给一个类添加很多的依赖。但是当使用构造器方式注入到了某个特定的点构造器中的参数变得太多以至于很明显地发现something is wrong。拥有太多的依赖通常意味着你的类要承担更多的责任明显违背了单一职责原则SRPSingle responsibility principle。这个问题在我司的项目代码真的很常见。依赖注入与容器本身耦合依赖注入框架的核心思想之一就是受容器管理的类不应该去依赖容器所使用的依赖。换句话说这个类应该是一个简单的POJO(Plain Ordinary Java Object)能够被单独实例化并且你也能为它提供它所需的依赖。这个问题具体可以表现在你的类和依赖容器强耦合不能在容器外使用你的类不能绕过反射例如单元测试的时候进行实例化必须通过依赖容器才能实例化这更像是集成测试不能使用属性注入的方式构建不可变对象(final 修饰的变量)Spring 开发团队的建议Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for mandatory dependencies and setter methods or configuration methods for optional dependencies.简单来说就是强制依赖就用构造器方式可选、可变的依赖就用 setter 注入当然你可以在同一个类中使用这两种方法。构造器注入更适合强制性的注入旨在不变性Setter注入更适合可变性的注入。让我们看看Spring 这样推荐的理由首先是基于构造方法注入The Spring team generally advocates constructor injection as it enables one to implement application components as immutable objects and to ensure that required dependencies are not null. Furthermore constructor-injected components are always returned to client (calling) code in a fully initialized state. As a side note, a large number of constructor arguments is a bad code smell, implying that the class likely has too many responsibilities and should be refactored to better address proper separation of concerns.Spring 团队提倡使用基于构造方法的注入因为这样一方面可以将依赖注入到一个不可变的变量中 (注final 修饰的变量)另一方面也可以保证这些变量的值不会是 null。此外经过构造方法完成依赖注入的组件 (注比如各个 service)在被调用时可以保证它们都完全准备好了。与此同时从代码质量的角度来看一个巨大的构造方法通常代表着出现了代码异味这个类可能承担了过多的责任。而对于基于 setter 的注入他们是这么说的Setter injection should primarily only be used for optional dependencies that can be assigned reasonable default values within the class. Otherwise, not-null checks must be performed everywhere the code uses the dependency. One benefit of setter injection is that setter methods make objects of that class amenable to reconfiguration or re-injection later.基于 setter 的注入则只应该被用于注入非必需的依赖同时在类中应该对这个依赖提供一个合理的默认值。如果使用 setter 注入必需的依赖那么将会有过多的 null 检查充斥在代码中。使用 setter 注入的一个优点是这个依赖可以很方便的被改变或者重新注入。参考Setter-based dependency injectionField Dependency Injection Considered HarmfulIDEA 警告 Field injection is not recommended
往期推荐
Spring中的重试功能嗯有点东西超级详细的Spring Boot 注解总结事务注解 Transactional 失效的3种场景及解决办法