为什么Spring不推荐使⽤@Autowired注解?
引⾔
在实际⼯作中,使⽤IDEA开发时,很多码友都喜欢使⽤@Autowired注解进⾏依赖注⼊,这个时候 IDEA 就会报黄⾊警告,代码⼀
⽚warning,代码洁癖的我不允许这么⼀个不明不⽩的警告在这⾥。@Autowired作为Spring的亲⼉⼦,为啥在IDEA中提⽰了这么⼀个警告?所以,带着我的洁癖,和我的好奇⼼,开始研究起了这个警告。
我们简单翻译⼀下⾃动提⽰的是啥意思:
不建议直接在字段上进⾏依赖注⼊。Spring开发团队建议:在Java Bean中永远使⽤构造⽅法进⾏依赖注⼊。
带着上⾯的疑问,我们接着往下看
依赖注⼊的⽅式
Spring有三种依赖注⼊的⽅式
基于属性(filed)注⼊
这种注⼊⽅式就是在 bean 的变量上使⽤注解进⾏依赖注⼊。本质上是通过反射的⽅式直接注⼊到 field 。这是我平常开发中看的最多也是最熟悉的⼀种⽅式。⽐如:
@Autowired
UserService userService;
基于set⽅法注⼊
通过对应变量的setXXX()⽅法以及在⽅法上⾯使⽤注解,来完成依赖注⼊。⽐如:
private UserService userService;
@Autowired
public void setUserService(UserService userService){
this.userService = userService;
}
说明:在 Spring 4.5 及更⾼的版本中,setXXX 上⾯的 @Autowired 注解是可以不写的。
基于构造器注⼊
将各个必需的依赖全部放在带有注解构造⽅法的参数中,并在构造⽅法中完成对应变量的初始化,这种⽅式,就是基于构造⽅法的注⼊。⽐如:
private final UserService userService;
@Autowired
public UserController(UserService userService){
this.userService = userService;
}
属性注⼊的问题
如你所见,变量(filed)注⼊的⽅式是如此的简洁。但实际上他是有⼀些问题的,具体问题如下:
问题⼀
@Autowired
private UserService userService;
private String company;
public UserServiceImpl(){
thispany = Company();
}
编译过程不会报错,但是运⾏之后报NullPointerException
Instantiation of bean failed; nested exception is BeanInstantiationException: Failed to[...]: Constructor threw excepti on; nested exception is NullPointerException
Java 在初始化⼀个类时,是按照静态变量或静态语句块 –> 实例变量或初始化语句块 –> 构造⽅法 -> @Autowired 的顺序。所以在执⾏这个类的构造⽅法时,user对象尚未被注⼊,它的值还是 null。
问题⼆
不能有效的指明依赖。相信很多⼈都遇见过⼀个 bug,依赖注⼊的对象为 null,在启动依赖容器时遇到这个问题都是配置的依赖注⼊少了⼀个注解什么的。这种⽅式就过于依赖注⼊容器了,当没有启动整个依赖容器时,这个类就不能运转,在反射时⽆法提供这个类需要的依赖。
问题三
依赖注⼊的核⼼思想之⼀就是被容器管理的类不应该依赖被容器管理的依赖,换成⽩话来说就是如果这个类使⽤了依赖注⼊的类,那么这个类摆脱了这⼏个依赖必须也能正常运⾏。然⽽使⽤变量注⼊的⽅式是不能保证这点的。
怎么解决?
我们⼀般开发需要注⼊属性的时候都会使⽤到这三个注解@Autowired、@Inject、@Resource,这三个注解在 Spring 中也是⽀持只⽤的。我们先来看⼀下这三个注解有啥区别?
@Autowired
@Autowired为 Spring 框架提供的注解,可以理解是 Spring 的亲⼉⼦。这⾥先给出⼀个⽰例代码
public interface IndexService {
void sayHello();
}
@Service
public class IndexServiceImpl implements IndexService {
@Override
public void sayHello(){
System.out.println("hello, this is IndexServiceImpl");
}
}
@Service
public class IndexServiceImpl2 implements IndexService {
@Override
public void sayHello(){
resource和autowired注解的区别System.out.println("hello, this is IndexServiceImpl2");
}
}
测试⽅法
@SpringBootTest
public class Stest {
@Autowired
// @Qualifier("indexServiceImpl2")
IndexService indexService;
@Test
void gooo(){
Assertions.assertNotNull(indexService);
indexService.sayHello();
}
}
这⾥说⼀下匹配 bean 的过程:
1. 按照type在上下⽂中查匹配,查type为IndexService的 bean。
2. 如果有多个 bean,则按照name进⾏匹配
1. 如果有@Qualifier注解,则按照@Qualifier指定的name进⾏匹配,查name为indexServiceImpl2的 bean。
2. 如果没有,则按照变量名进⾏匹配。查name为indexService的 bean 。
3. 匹配不到,则报错。@Autowired(required=false),如果设置required为 false (默认为 true ),则注⼊失败时不会抛出异常。
@Inject
在 Spring 的环境下,@Inject 和 @Autowired 是相同的,因为它们的依赖注⼊都是使⽤AutowiredAnnotationBeanPostProcessor这个后置处理器来处理的。
这两个的区别,⾸先 @Inject 是 Java EE 包⾥的,在SE环境需要单独引⼊。另⼀个区别在于 @Autowired 可以设置 required=false ⽽@Inject 并没有这个属性。也有的说 @Inject 是 spring 的⼲⼉⼦。
@Resource
@Resource 是JSR-250定义的注解。Spring 在 CommonAnnotationBeanPostProcessor 实现了对JSR-250的注解的处理,其中就包括 @Resource。
这个@Resource有 2 个属性name和type。在 spring 中name属性定义为 bean 的名字,type这是 bean 的类型。如果属性上加
@Resource 注解那么他的注⼊流程是:
1. 如果同时指定了name和type,则从 Spring 上下⽂中到唯⼀匹配的 bean 进⾏装配,不到则抛出异常。
2. 如果指定了name,则从上下⽂中查名称匹配的 bean 进⾏装配,不到则抛出异常。
3. 如果指定了type,则从上下⽂中到类型匹配的唯⼀ bean 进⾏装配,不到或是到多个,都会抛出异常。
4. 如果既没有指定name,⼜没有指定type,则默认按照byName⽅式进⾏装配;如果没有匹配,按照byType进⾏装配。
所以我们可以使⽤@Resource替代@Autowired,当然也可以使⽤@RequiredArgsConstructor构造器⽅式注⼊,这种形式就是Spring 推荐使⽤的构造器⽅式注⼊,此种⽅式是lombok包下的注解,如果使⽤此种⽅式,需要项⽬中引⼊lombok,例如:
@RequiredArgsConstructor
public class UserDaoImpl {
private final User user;
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论