Java中@Resource 和 @Autowired 的区别

@Resource

@Resource是Java自己的注解

先来看一下源码:
image-1664427965173
@Resource有两个属性是比较重要的,分别是name和type;Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。默认按name进行注入。

下面看一下这样的一个变量注入方式:
image-1664427987243
按type进行注入的自动注入策略,这个type指的就是类的类型,可以这样理解,比如Apple.class,类型就是Apple,Person.class,类型就是Person。
image-1664428003012
如果我有两个Person类,这个时候就无法辨别注入对象,就可以使用name属性去辨别。

在spring中, 被@Component标识的类会在servlet容器启动时加载单实例(默认设置下),用@Resource注解可以注入,如果需要的bean没有被提前加载, 则会报错。

在加载的时,是根据类名(不包括package地址)判断,出现重复的类名会报异常(不在同一个package, 也会报异常)。如果类名相同,可以标识成不同的bean,然后@Resource的name去辨别。

假设,有两个相同的实现类,那么可以用name去辨别,如下:

@Service("a")
public class ReportServiceImpl implements ReportService{
    ......
}

@Service("b")
public class ReportServiceImpl implements ReportService{
    ......
}

注入的时候指定名称为a的那个实现类:
image-1664428053349
这样指定,就知道是哪一个了。

@AutoWired

@AutoWired是spring的注解,Autowired只根据type进行注入,不会去匹配name。如果涉及到type无法辨别注入对象时,那需要依赖@Qualifier或@Primary注解一起来修饰。@Resource默认按名称方式进行bean匹配,@Autowired默认按类型方式进行bean匹配。

使用@AutoWired变量注解方式时,会有黄色波浪线,idea会提示:
image-1664428090089
Spring团队建议:“在bean中始终使用基于构造函数的依赖注入。始终对强制依赖项使用断言”。
意思是说,用@AutoWired的注入时,尽量用基于构造函数的依赖注入,而不是变量的方式注入。
这就是构造函数方式的依赖注入:
image-1664428122074
再来看一下@AutoWired注解的源码:
image-1664428134082
只有required属性,没有其他属性了,根据type进行注入。
用@AutoWired的变量注入时,如果碰到无法分辨的对象,就无法注入成功。但是可以结合@Qualifier注解使用,表明哪个实现类才是我们需要的。
image-1664428155600
但是仍然不建议使用变量注入方式。
1、可能会造成NPE,如下:

public class TestController {
  @Autowired
  private TestService testService;
  private String name;
  public TestController(){
    this.name= testService.getName();
  }
 }

这段代码执行时会报NPE。Java类会先执行构造函数,然后在通过@Autowired注入实例,二构造函数里面需要注入的对象,因此在执行构造函数的时候就会报错。

2、还可能回导致循环依赖,即A里面注入B,B里面又注入A。

注:在代码中发现构造方法中注入了很多依赖,显得很臃肿,对于这个问题,说明类中有太多的责任,违反了类的单一性职责原则,这时候需要考虑使用单一职责原则进行代码重构。

总结

简单来说,这两的区别就是:

  1. @Resource:java的注解,属性较多,type无法分辨时可以用name分辨
  2. @Autowired:spring的注解,一个属性,type无法分辨时需要借助@Qualifier注解才能使用,使用@Autowired方式最好使用构造函数的方式注入。