这篇文章主要介绍了Spring中@Autowired、@Resource和@Inject注解的区别是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Spring中@Autowired、@Resource和@Inject注解的区别是什么文章都会有所收获,下面我们一起来看看吧。
javax.annotation.Resource
jdk 内置的,JSR-250 中的注解。
依赖注入通过 org.springframework.context.annotation.CommonAnnotationBeanPostProcessor 来处理。
org.springframework.beans.factory.annotation.Autowired org.springframework.beans.factory.annotation.Value
javax.inject.Inject
JSR-330 中的注解,作用同 @Autowired
依赖注入通过 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor 来处理。
org.springframework.beans.factory.annotation.Qualifier javax.inject.Qualifier
依赖注入通过 org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver 来处理。
@Autowired
spring 自带的注解。
注入顺序
按照 type 在 上下文中查找匹配的 bean
如果有多个 bean,按照 name 进行匹配
如果有 @Qualifier 注解,按照 @Qualifier 指定的 name 进行匹配
如果没有,按照变量名进行匹配
匹配不到,报错。因为 required 默认为 true,不想注入设置此 bean @Autowired(required=false)。
@Inject
在 spring 中,@Inject 和 @Autowired 相同。
@Inject 和 @Autowired 区别
@Inject 是 javaee 6 及以上版本包里的。
@Autowired 可以设置 required=false 而 @Inject 没有这个属性。
@Resource
有两个重要的属性,name 和 type,spring 将 name 属性解析为 bean 的名字,type 解析为 bean 的类型。如果未指定 name,取变量名给 name 赋值。
CommonAnnotationBeanPostProcessor 中Resource 赋值源码
/** * Class representing injection information about an annotated field * or setter method, supporting the @Resource annotation. */ private class ResourceElement extends LookupElement { private final boolean lazyLookup; public ResourceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) { super(member, pd); Resource resource = ae.getAnnotation(Resource.class); String resourceName = resource.name(); Class<?> resourceType = resource.type(); this.isDefaultName = !StringUtils.hasLength(resourceName); if (this.isDefaultName) { resourceName = this.member.getName(); if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) { resourceName = Introspector.decapitalize(resourceName.substring(3)); } } else if (embeddedValueResolver != null) { resourceName = embeddedValueResolver.resolveStringValue(resourceName); } if (Object.class != resourceType) { checkResourceType(resourceType); } else { // No resource type specified... check field/method. resourceType = getResourceType(); } this.name = (resourceName != null ? resourceName : ""); this.lookupType = resourceType; String lookupValue = resource.lookup(); this.mappedName = (StringUtils.hasLength(lookupValue) ? lookupValue : resource.mappedName()); Lazy lazy = ae.getAnnotation(Lazy.class); this.lazyLookup = (lazy != null && lazy.value()); } @Override protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) { return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) : getResource(this, requestingBeanName)); } }
在变量名相同的情况下报错
The bean could not be injected as a because it is a JDK dynamic proxy that implements:
指定了不同type无法解决问题,跟进源码发现是 spring boot 把异常给处理了
org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'productInit':
Injection of resource dependencies failed;
nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException:
Bean named 'example2ProductMapper' is expected to be of type 'com.alibaba.cloud.youxia.manager.ProductManager' but was actually of type 'com.sun.proxy.$Proxy47'
想到在 DefaultListableBeanFactory 中 beanDefinitionMap 按照名称和 BeanDefinition 键值对的问题,名称和注入的对象一一对应,不然就会出现不对应的问题
注入规则
如果未指定 name,取变量名从上下文中查找名称匹配的 bean 进行注入,找不到或者注入的变量名与类型无法对应抛出异常。
如果指定了 name,则从上下文中查找名称匹配的 bean 进行注入,找不到抛出异常。
如果指定了 type,有两种情况
通过变量名从上下文中查找不到对应的 bean,则通过 type则从上下文中查找类型匹配的 bean 进行注入,找不到抛出异常。
通过变量名从上下文中找到对应的 bean但是注入的类型与无法与DefaultListableBeanFactory 中 beanDefinitionMap中通过变量名得到的 BeanDefinition 类型一致,抛出异常。
既没有指定 name,又没有指定 type,默认按照变量名进行注入。
如果同时指定了 name 和 type,从上下文中找到唯一匹配的 bean 进行注入,找不到抛出异常。
匹配顺序为
变量名 → 指定的 name → 指定的 type
如果注入的 bean 变量名相同,但是类型不同,通过 name 指定是修改代码量最小的办法。