@Resource和@Autowired的区别

本文对比了Java注解@Resource与Spring注解@Autowired的异同,着重讲解了两者在名称匹配、默认注入策略和特殊情况下的应用。Resource支持byName和byType,Autowired默认byType,讲解了如何在接口多实现类场景下正确使用注解避免错误。
摘要由CSDN通过智能技术生成

@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一样)。

### 回答1: @Resource和@Autowired都是用来注入依赖的注解,但是它们有一些不同点: 1. @Resource是JDK提供的注解,而@AutowiredSpring提供的注解。 2. @Resource默认按照名称进行装配,如果找不到对应名称的bean,则会按照类型进行装配。而@Autowired默认按照类型进行装配,如果找不到对应类型的bean,则会抛出异常。 3. @Resource可以指定名称进行装配,例如@Resource(name="myBean"),而@Autowired没有这个功能。 4. @Resource只能注入其他bean,而@Autowired可以注入其他组件,例如配置文件、消息队列等。 总的来说,两者都可以用来注入依赖,但是在使用时需要注意它们的不同点。 ### 回答2: @Resource和@Autowired都是Spring框架中用于注入依赖的注解,不同之处在于它们的实现方式和使用场景。 @Resource是JDK自带的注解,它可以通过名称或类型进行注入。当没有指定名称时,它默认按照字段或属性名作为组件名称来查找依赖对象,如果找不到匹配的组件,则会抛出NoSuchBeanDefinitionException异常。当指定了名称时,则按照名称查找依赖对象。如果多个对象匹配同一名称,则会抛出NoUniqueBeanDefinitionException异常。@Resource注解的属性name和type用于指定依赖对象的名称和类型。 @AutowiredSpring的注解,它根据类型进行注入。如果有多个类型匹配的对象,则会根据名称进行区分,如果名称也有多个匹配,则会抛出NoUniqueBeanDefinitionException异常。@Autowired注解的属性required用于指定依赖对象是否必须存在,默认为true。 另外,@Autowired注解还可以用在构造函数、set方法和方法参数上,而@Resource只能用在字段或属性上。使用@Autowired注解需要在配置文件中增加<context:component-scan>元素,<context:component-scan>会自动扫描指定包下的组件,并将标有@Autowired注解的依赖对象注入到相应的组件中。 总的来说,@Resource注解更擅长按照名称查找依赖对象,而@Autowired注解更擅长按照类型查找依赖对象。建议在开发中,根据具体场景选择合适的注解来实现依赖注入。 ### 回答3: 在Spring框架中,@Resource和@Autowired都是用于自动注入bean的注解。但是它们之间有什么区别呢? 区别如下: 1. @Resource来自javax.annotation包,而@Autowired来自Spring框架。 2. @Resource默认按名称进行装配,即找到和成员变量名称相同的bean进行注入。而@Autowired默认按类型进行装配,即如果有多个同类型的bean,就要用@Qualifier进行指定。 3. @Resource能够指定bean的名称和类型,@Autowired不支持指定名称,只能指定类型。 4. @Resource可以自动注入java原生类型和String类型,因为它们底层都用到了Java提供的反射机制。但@Autowired只能自动注入bean类型。 5. @Autowired支持可选属性(required=false),即如果找不到bean,不会抛出异常。但@Resource不支持可选属性,如果找不到bean就会抛出异常。 所以,总结起来,@Resource更加灵活,支持指定bean的名称和类型,并且能够自动注入java原生类型和String类型。而@Autowired更加简洁,适用于注入bean类型,而且支持可选属性。根据具体场景的不同,选择不同的注解可以更好地实现自动注入bean的功能。
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值