前言
最近写代码的时候,碰到一个小问题;
可以看到,Spring Boot不推荐使用@Autowired
的方式进行属性注入。
我之前一直这样写,从来没觉得这有什么问题,看到这个报错,心里咯噔一下,然后忽然想,@Autowired 与@Resource有什么不同。
然后我发现,我竟然完全不记得了。
因此,写这篇笔记记录一下。
先解释一下,为什么Spring Boot不推荐使用@Autowired
的方式进行属性注入。
-
@Autowired
是spring定义的注解,与Spring框架强耦合,换成别的框架,可能不支持。 -
@Resource
是JSR-250定义的注解,是Java标准,支持几乎所有java框架。
换言之,@Autowired
能用的,@Resource
也能用,@Autowired
不能用的,@Resource
还能用。
不过现在java程序员几乎等同于Spring程序员,说这个,似乎没有必要。
简单举例
LaymanController.java
@Controller
public class LaymanController {
@Autowired
public LaymanService laymanService;
@GetMapping(value = "/sayHello")
public void sayHello() {
laymanService.sayHello();
}
}
LaymanService
public interface LaymanService {
void sayHello();
}
LaymanServiceImpl
@Service
public class LaymanServiceImpl implements LaymanService{
public void sayHello(){
System.out.println("你好,我是layman");
}
}
@Autowired
@Autowired
默认按类型注入,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false。
public @interface Autowired {
# 注册时该类型必须存在,如果不存在则报错。
boolean required() default true;
}
如果注入的类型有可能为null,可以这样配置:@Autowired(required = false)
言归正传,还是说下,下面这个例子的加载顺序:
@Autowired
public LaymanService laymanService;
项目启动后,由于我们在LaymanServiceImpl
上配置了@Service
注解,那么它会被CompentScan
扫描进容器内,注册为bean。
由于未指定name属性,使用默认值(类名首字母转小写)为bean命名(laymanServiceImpl
)
@Autowired
查找类型为LaymanService
的bean,如果找不到,则找名称为laymanService
的bean。
很明显,有类型为LaymanService
的bean,名为laymanServiceImpl
(多态),注入成功!
做个小改动,让它报个错
此时我们再添加一个类,叫做LaymanServiceImpl2
LaymanServiceImpl2.java
@Service
public class LaymanServiceImpl2 implements LaymanService{
public void sayHello(){
System.out.println("你好,我是layman2");
}
}
此时,IOC容器中,存在两个类型为LaymanService的bean,一个名为laymanServiceImpl
,另一个名为laymanServiceImpl2
。
那么,项目启动会报错,为什么?
因为@Autowired
默认按类型匹配,而且只匹配一个
,现在找到两个,它不知道应该注入哪个?
于是它纠结,迷茫,最后一咬牙,还是报错吧。
如何解决该BUG
使用@Qualifier
注解。
修改代码。
LaymanServiceImpl2改动如下:
@Service("laymanService")
public class LaymanServiceImpl2 implements LaymanService{
public void sayHello(){
System.out.println("你好,我是layman");
}
}
@Service("laymanService")
为LaymanServiceImpl2 指定bean的name属性
。
LaymanController.java 改动如下:
@Autowired
@Qualifier("laymanService")
public LaymanService laymanService;
@Autowired扫描到两个类型为LaymanService 的bean,但是@Qualifier指定了bean的名称,因此它会去查找名为laymanService的bean,找到一个,装配成功!
@Autowired加载顺序图
@Resource
@Resource默认按名称
进行装配,且有两个极其重要的属性:name和type
Spring将@Resource注解的name属性解析为bean的名字,type属性则解析为bean的类型。
逻辑
如果既不指定name,也不指定type,则按照Name进行装配(默认为变量名)
如果指定name,则从上下文中查找名称(id)唯一匹配
的bean进行装配,找不到则抛出异常
如果指定type,则从上下文中找到类型唯一匹配
的bean进行装配,找不到或者找到多个,抛出异常
如果同时指定name和type,则从Spring上下文中找到唯一匹配
的bean进行装配,找不到则抛出异常
// 默认查找名为laymanService的bean,如果找到唯一一个,则匹配,如果找不到,再按类型去查找
@Resource
public LaymanService laymanService;
@Resource的装配顺序
-
既不指定name,也不指定type
-
指定name
-
指定type
-
同时指定name和type
为什么不推荐使用@Autowired,可是依然那么人用
- 历史遗留问题(以前就这一种方式,开发用习惯了,改不了。)
- 省事,bean的名字不用写。(按类型匹配,只要扔个
@service
就行了),@resource要写@service("service_name")
,否则就要两次查找,浪费性能。(@resource先找name,因为默认名不匹配,所以找不到,还会再找type,而@Autowired直接找type)
但是,架构设计考虑性能的话,推荐使用@resource
。
一是因为耦合度,契合大部份java框架。
二是写上@service("service_name")
,访问速度会很快,毕竟可以通过name精准搜索。
缺点是,每个bean都要手动添加name属性。
补充
@Autowired
是commonAnnotationBeanPostProcessor
处理器负责处理,而@Resource
是autowiredAnnotationBeanPostProcessor
处理器负责处理。
有兴趣的可以看看源码:
推荐的源码博客:https://blog.csdn.net/ijavaweb/article/details/25553231