@Resource和@Autowired的区别:

一、@Resource

@Resource是Java自己的注解
@Resource有两个属性是比较重要的,分别是name和type,默认按byName进行注入,如果无法通过名称匹配到对应的 Bean 的话,注入方式会变为byType;Spring将@Resource注解的name属性解析为bean的名称,type属性解析为bean的类型,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略,如果同时指定name 和type属性(不建议这么做)则注入方式为byType+byName。
来看一下源码:
在这里插入图片描述

protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)
      throws NoSuchBeanDefinitionException {
 
   Object resource;
   Set<String> autowiredBeanNames;
   String name = element.name;      // 获取到@Resource中name属性的值
 
   // 因为这里是使用Spring上下文的的beanFactory,所以为true
   if (factory instanceof AutowireCapableBeanFactory) {
       
      // 转换一下bean工厂
      AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;
 
      // 获取到依赖注入的描述类,也就是封装一些信息
      DependencyDescriptor descriptor = element.getDependencyDescriptor();
 
      // 这里重点来了
      // 这里会判断是否使用了默认名字,即@Resource的name属性的值
      // 第三个条件就是name是否在bean工厂中存在,name就是@Resource的name属性值,如果没写的话就是字段的name
      // 第三个条件就是第二个条件为true的时候会执行,当用户没写@Resource的name的时候就会去判断接口名在bean工厂里面是否存在,如果不存在就再根据Type类型来找。
      if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {
         autowiredBeanNames = new LinkedHashSet<>();
         resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
         if (resource == null) {
            throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
         }
      }
 
      // 这个else就是使用了@Resource中name属性,所以使用了name属性就直接去容器中创建或者从容器中取bean缓存
      else {
         resource = beanFactory.resolveBeanByName(name, descriptor);
         autowiredBeanNames = Collections.singleton(name);
      }
   }
   else {
      resource = factory.getBean(name, element.lookupType);
      autowiredBeanNames = Collections.singleton(name);
   }
 
   if (factory instanceof ConfigurableBeanFactory) {
      ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) factory;
      for (String autowiredBeanName : autowiredBeanNames) {
         if (requestingBeanName != null && beanFactory.containsBean(autowiredBeanName)) {
            beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);
         }
      }
   }
 
   return resource;
}

按type进行注入的自动注入策略,这个type指的就是类的类型,可以这样理解,比如Apple.class,类型就是Apple,Person.class,类型就是Person。
按name进行注入的自动注入策略,这个name指的就是类的名称,可以这样理解,比如Apple.class,名称就是apple,PersonServiceImpl.class,名称就是personServiceImpl,通常首字母小写。


当一个接口存在多个同名实现类的情况下:
如果类名相同,可以标识成不同的bean,然后用@Resource的name去辨别。
假设,有两个相同的实现类(不同包),那么可以用name去辨别,如下:

@Service("a")
public class EmployeeServiceImpl implements EmployeeService{
    ......
}

@Service("b")
public class EmployeeServiceImpl implements EmployeeService{
    ......
}

注入的时候指定名称为a的那个实现类:

	@Resource(name = "a")
    private EmployeeService employeeService;

如果不指定就会报错:
在这里插入图片描述
当然,也可以直接注入EmployeeServiceImpl,这个实现类在不同包,也不会报错,不过注入的对象就是EmployeeServiceImpl类型了。


当一个接口存在多个不同类名实现类的情况下:
如:EmployeeService有两个实现类EmployeeServiceImpl和EmployeeServiceImpl2

@Service
public class EmployeeServiceImpl implements EmployeeService{
    ......
}

@Service
public class EmployeeServiceImpl2 implements EmployeeService{
    ......
}

错误注入
如下这样注入,会报错(编译通过但是运行报错),默认按名称匹配,分别是employeeServiceImpl和employeeServiceImpl2,跟employeeService匹配不上,按类型匹配,都是EmployeeService类型,容器里也没有(接口一般是不加@Service注解的,还是会去找实现类的名称或类型)。

 	//错误,编译通过但运行报错,byName和byType都无法匹配到bean
 	@Resource
    private EmployeeService employeeService;

在这里插入图片描述

正确注入:

	//正确,注入名称为employeeServiceImpl2的bean
    @Resource
    private EmployeeService employeeServiceImpl;

    //正确,注入名称为employeeServiceImpl2的bean
    @Resource
    private EmployeeService employeeServiceImpl2;

	//正确,注入名称为employeeServiceImpl2的bean
    @Resource(name = "employeeServiceImpl2")
    private EmployeeService employeeService;

二、@AutoWired

@AutoWired是spring的注解,默认的注入方式为byType(根据类型进行匹配),也就是说会优先根据接口类型去匹配并注入 Bean (接口的实现类),当一个接口存在多个实现类的话,byType这种方式就无法正确注入对象了,因为这个时候 Spring 会同时找到多个满足条件的选择,默认情况下它自己不知道选择哪一个,这个时候,注入方式会变为 byName(根据名称进行匹配),这个名称通常就是类名(首字母小写)。另外,如果type无法辨别注入对象时,也可以配合@Qualifier或@Primary注解来分辨注入类。


当一个接口存在多个同名实现类的情况下:
如果类名相同,标识成不同的bean,然后用@Resource的name去辨别。
假设,有两个相同的实现类(不同包),那么可以用name去辨别,如下:

@Service("a")
public class EmployeeServiceImpl implements EmployeeService{
    ......
}

@Service("b")
public class EmployeeServiceImpl implements EmployeeService{
    ......
}

注入的时候配合@Qualifier注解指定名称为a的那个实现类:

	@Autowired
    @Qualifier(value = "b")
    private EmployeeService employeeService;

如果不指定就会报错:

在这里插入图片描述
当然,也可以直接注入EmployeeServiceImpl,这个实现类在不同包,也不会报错,不过注入的对象就是EmployeeServiceImpl类型了。


当一个接口存在多个不同类名实现类的情况下:
如:EmployeeService有两个实现类EmployeeServiceImpl和EmployeeServiceImpl2
错误注入
如下这样注入,会报错(编译就会报错),默认按类型匹配,都是EmployeeService类型,再按名称匹配,分别是employeeServiceImpl和employeeServiceImpl2,跟employeeService也匹配不上。

	//EmployeeService有两个实现类EmployeeServiceImpl和EmployeeServiceImpl2
    //错误,这个时候注入会报错,这个时候byName和byType都无法匹配到bean
    @Autowired
    private EmployeeService employeeService;

在这里插入图片描述
正确注入方式1:
注入时,变量名称写对,跟容器中的bean名称一样,这就是按名称匹配了,如下:

    //正确,注入名称为employeeServiceImpl的bean
    @Autowired
    private EmployeeService employeeServiceImpl;

    //正确,注入名称为employeeServiceImpl2的bean
    @Autowired
    private EmployeeService employeeServiceImpl2;

正确注入方式2:
配合@Qualifier注解,如下:

	@Autowired
    @Qualifier(value = "employeeServiceImpl2")
    private EmployeeService employeeService;

建议通过 @Qualifier 注解来显式指定名称而不是依赖变量的名称。


@Autowired方式最好使用构造函数的方式注入

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

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

三、总结

简单来说,这两的区别就是:
@Resource:
java的注解,属性较多,默认的注入方式为byName(根据名称进行匹配),type无法分辨时,可以根据name分辨,通过name属性来显式指定Bean名称。
@Autowired:
spring的注解,一个属性,默认的注入方式为byType(根据类型进行匹配),type无法分辨时,可以根据name分辨,变量名称要与Bean名称一致,也可以通过@Qualifier 注解来显式指定Bean名称。


很简单的一个例子,有两个苹果,一个叫哈哈,一个叫呵呵,你指着两个苹果,意思是去拿个苹果,让@Resource去拿,如果不说明,他懵了,但是你说明拿叫哈哈的那个,他就知道了,给你拿来了,让@Autowired去拿,如果不说明,他也懵了,但是他又是个聋子,听不到你说的,结果就拿不到,但是如果写了个字条(@Qualifier)写明拿呵呵,他也就知道了。
另外的情况就是,不管是@Resource或者@Autowired,如果你指苹果的时候就指的很明确,直接指到叫哈哈的苹果,他们也都能拿到(变量名称跟bean一样)。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐