@Resource和@Autowired的区别-程序员宅基地

技术标签: spring  spring boot  java  注解  

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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/mrxutada/article/details/118811930

智能推荐

javaee 路径问题_javaee的路径问题-程序员宅基地

文章浏览阅读134次。getServletPath():获取能够与“url-pattern”中匹配的路径,注意是完全匹配的部分,的部分不包括。getPathInfo():与getServletPath()获取的路径互补,能够得到的是“url-pattern”中d的路径部分getContextPath():获取项目的根路径getRequestURI:获取根路径到地址结尾getRequestURL:获取请求的地址链..._javaee的路径问题

树形dp(poj 1947 Rebuilding Roads )-程序员宅基地

文章浏览阅读71次。题意: 有n个点组成一棵树,问至少要删除多少条边才能获得一棵有p个结点的子树?思路: 设dp[i][k]为以i为根,生成节点数为k的子树,所需剪掉的边数。 dp[i][1] = total(i.son) + 1,即剪掉与所有儿子(total(i.son))的边,还要剪掉与其父亲(+1)的边。 dp[i][k] = min(dp[i][k],dp[i][j - k] +...

顺序栈链栈基本操作及应用-数据结构类C语言_c语言中链栈怎么清空栈-程序员宅基地

文章浏览阅读844次。顺序栈链栈C语言代码_c语言中链栈怎么清空栈

stm32f103x8最小系统板原理图pcb_stm32f103x8最小系统原理图-程序员宅基地

文章浏览阅读432次。原理图如下 刚开始学习画pcb的四层板子,做了一个stm32f103x8的系统板。封装基本手绘,比较粗糙。_stm32f103x8最小系统原理图

调用android自带模块-程序员宅基地

文章浏览阅读45次。1,调web浏览器Uri myBlogUri = Uri.parse("http://xxxxx.com");returnIt = new Intent(Intent.ACTION_VIEW, myBlogUri);2,地图Uri mapUri = Uri.parse("geo:38.899533,-77.036476");returnIt = new Intent(Intent.ACTION_V..._returnit = new intent(intent.action_dial, teluri);

华为鸿蒙的代码是多少,消息称华为鸿蒙此次开源代码量大约是8GB,AOSP超60GB-程序员宅基地

文章浏览阅读394次。来源:IT之家在近期的华为鸿蒙伙伴峰会上,华为消费者业务 AI 与智慧全场景业务部副总裁杨海松接受媒体采访时表示,鸿蒙系统完全开源开放,今年会按照既定计划做到 128MB-4GB 的设备全栈系统开源开放。“欢迎三方的手机厂商使用鸿蒙系统,一起开源共建。”不过,“三方手机 ‘谁’,以及 ‘什么时候’上鸿蒙系统,完全取决于厂商的自己商业考量。”微博博主 @钊哥科普 今天爆料称,鸿蒙此次开源的代码量大约..._鸿蒙卫星发送短消息代码分析

随便推点

使用react-pdf预览pdf_react pdf预览-程序员宅基地

文章浏览阅读4.3k次。在使用构建的react项目中如何实现预览??如果PDF中包含印章该如何使印章展示出来???我项目中使用的版本为5.2.0。4. 显示印章客户在浏览器中使用开源PDF.JS插件预览签署后的PDF文件时会遇到无法显示电子签章的情况,这是因为PDF.JS因无法进行校验电子签名故而默认隐藏了电子签章。如果需要显示电子签章则需要在中找到以下代码并进行注释或者某些版本的PDF.JS需要在pdf.worker.js中找到如下代码并进行注释最后看下效果图......_react pdf预览

牛客网编程题-超级子串_牛客网编程题解析-程序员宅基地

文章浏览阅读1k次。题目如下:代码:_牛客网编程题解析

柯马机器人示教器编程_柯马机器人示教器C5G-TP5WC CR17910085全新原装进口现货销售...-程序员宅基地

文章浏览阅读776次。产品参数品牌:柯马comau成色:全新/二手数量:长期备有现货价格:电议柯马机器人示教器C5G-TP5WC CR17910085备件找本公司,全新二手备件一应俱全,您不需要停产,不需要等待。广州广科智能技术有限公司专业工业机器人服务商,服务项目包含:工业机器人销售、机器人备件、机器人维修、机器人保养、机器人调试、机器人改造和机器人培训等,主要针对ABB、库卡KUKA、发那科FANUC、安川、川崎..._机器人示教器 常用品牌

openlayer3 常用梳理-设置点的样式_openlayers 移入坐标点设置样式-程序员宅基地

文章浏览阅读3.4k次。feature 对象中的点设置样式:var startMarker = new ol.Feature({ type: 'start', geometry: new ol.geom.Point(routeCoords[0]) });'start': new ol.style.Style({ image: new _openlayers 移入坐标点设置样式

Enhancing The Reliability of Out-of-distribution Image Detection in Neural Networks解读-程序员宅基地

文章浏览阅读1.1k次。[40]Liang S, Li Y, Srikant R. Enhancing The Reliability of Out-of-distribution Image Detection in Neural Networks[C]//International Conference on Learning Representations. 2018.1.摘要1.1问题:我们考虑了在神经网络中检测分布外图像的问题。1.2方法:我们提出了ODIN,这是一种简单有效的方法,不需要对预先训练的神经网络_enhancing the reliability of out-of-distribution image detection in neural n

苹果手机备份用什么软件好,iphone备份软件有什么用 苹果手机备份怎么操作 iPhone数据备份常用的3种方法_备份苹果手机用什么软件-程序员宅基地

文章浏览阅读683次,点赞20次,收藏12次。当苹果手机需要进行刷机、恢复出厂设置、降级iOS系统等操作时,我们需要将自己的。特别是在苹果发布新iOS系统时,总有一些小伙伴因为升降级系统,而导致了。iPhone中储存着重要的照片、通讯录、文件等数据,建议,避免因一些意外情况造成数据的丢失。本文会给大家介绍苹果手机备份用什么软件好,iphone备份软件有什么用的相关内容,希望本文的内容会对大家有所帮助。使用iCloud备份。_备份苹果手机用什么软件