- 联系方式:1761430646@qq.com
- 编写时间:2022年12月22日16:36:24
- 博客地址:www.zeroeden.cn
- 菜狗摸索,有误勿喷,烦请联系
前言
- 至少得知道
@Autowired和@Resouce注解都是去注入Bean的 @Autowired是默认通过byType注入- 而
@Resouce是默认通过byName注入 - 这里基本知道总执行流程就完事了,具体不会怎么举例子做演示
环境准备
-
准备一个
MyService接口,并有三个实现类/** * @author: Zero * @time: 2022/12/5 * @description: */ public interface MyService { public void show(); }
-
准备一个控制层,并且注入实现类
MyService接口的Bean/** * @author: Zero * @time: 2022/12/9 * @description: 控制层 */ @RestController public class TestController { @Autowired private MyService myService; @GetMapping("/test") public String test() { myService.show(); System.out.println("====这是控制层test()方法===="); return "success"; } }
1. @Autowired
1.1 牵扯到的其他两个注解
1.1.1 @Qualifier
- 当使用
@Autowired注解注入某个类型的Bean时 - 如果此时在
IOC容器中,此类型的Bean有多个时 - 可以通过
@Qualifier注解的属性指定Bean的名称

- 可以从源代码上看到,注解
@Quilifier上只有一个默认值为""的value属性,就是用来指定Bean的名称
1.1.2 @Primary
- 可以使用注解
@Primary,加在某个Bean对应的类上做个标记 - 被注解
@Primary标记的类,意味着当同类型的Bean有多个时,会优先注入当前这个

1.2 总执行流程
-
下面先给出一张流程图来示意注解
@Autowired注入Bean时的最终执行流程
-
由于流程分支走向实在很多,我在图上主要用带圆圈的数字标号标记了一些常用的,比较重要的点,后面也是主要讲这些点,一些比较简单的就不理了(比如说只有单个
Bean的类型刚好匹配,且没有@Qualifier注解约束,这时无疑就是直接注入这个Bean了)
1.3 注意点
1.3.1 @Qualifier的优先级比@Primary高
-
当某个类型的
Bean有多个时 -
就会先去找有无配注解
@Qualifier,有的话就按照注解@Qualifier配置的Bean名称来匹配 -
在没有配置注解
@Qualifier时才会去看是否配了注解@Primary来进行匹配Bean -
下面可以进行一个简单的小演示
-
就是既配置了注解
@Qualifier,又配置了注解@Primary -
配置如下所示


-
启动程序,访问接口
/test,其实验结果如下
-
可以看到实际上注入的是注解
Qualifier配置的名称为myServiceImplB的Bean -
也就是在即配了注解
@Qualifier,又配了注解@Primary的情况下,注入时@Qualifier的优先级是高于@Primary的
1.3.2 意思太长不知如何表达
-
就是在使用注解
@Autowired去注入Bean时 -
如果符合类型的
Bean有多个 -
并且不存在注解
@Qualifier,不存在注解@Primary时 -
此时会待注入对象的变量名称默认为要注入的
Bean名称,通过此名称去寻找符合的Bean -
如果找不到,不管注解
@Autowired的required属性是true还是flase,直接抛异常 -
这个也就是总执行流程图中标识的第3点
-
下面可以简单演示下,配置如下所示

-
各种情况分类
-
待注入对象的变量名称刚好有对应的
Bean-
Controller层配置如下
-
启动程序,访问接口
/test,实验结果如下
-
可以看到能够正常启动程序,能够正常注入对应名称的
Bean,业务也能够正常走动
-
-
待注入对象的变量名称无对应的
Bean,且注解@Autowired的属性required为true(默认值也是为ture)-
Controller层配置如下
-
可以此时找不到对应名称的
Bean,IDEA工具就直接给我们报错了(IDEA牛逼!!!) -
尝试启动程序

-
可以看到启动失败,注意此时并不是说找不到名称为
myServiceImplD的Bean,而是报发现多个符合Bean,但不知道注入哪个的问题
-
-
待注入对象的变量名称无对应的
Bean,且注解@Autowired的属性required为false(默认值也是为ture)-
Controller层如下
-
可以看到跟上述例子最大区别就是注解
@Autowired的属性required值为false -
但是
IDEA上此时还是很明显的报错 -
尝试启动程序

-
启动失败,报错跟上述例子一模一样
-
可以得知此时在这种情况下,注解
@Autowired的属性required无论是true还是false都会直接报错
-
-
2. @Resource
-
如下是注解
@Resource的源码package javax.annotation; import java.lang.annotation.*; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.*; /** * The Resource annotation marks a resource that is needed * by the application. This annotation may be applied to an * application component class, or to fields or methods of the * component class. When the annotation is applied to a * field or method, the container will inject an instance * of the requested resource into the application component * when the component is initialized. If the annotation is * applied to the component class, the annotation declares a * resource that the application will look up at runtime. <p> * * Even though this annotation is not marked Inherited, deployment * tools are required to examine all superclasses of any component * class to discover all uses of this annotation in all superclasses. * All such annotation instances specify resources that are needed * by the application component. Note that this annotation may * appear on private fields and methods of superclasses; the container * is required to perform injection in these cases as well. * * @since Common Annotations 1.0 */ @Target({TYPE, FIELD, METHOD}) @Retention(RUNTIME) public @interface Resource { /** * The JNDI name of the resource. For field annotations, * the default is the field name. For method annotations, * the default is the JavaBeans property name corresponding * to the method. For class annotations, there is no default * and this must be specified. */ String name() default ""; /** * The name of the resource that the reference points to. It can * link to any compatible resource using the global JNDI names. * * @since Common Annotations 1.1 */ String lookup() default ""; /** * The Java type of the resource. For field annotations, * the default is the type of the field. For method annotations, * the default is the type of the JavaBeans property. * For class annotations, there is no default and this must be * specified. */ Class<?> type() default java.lang.Object.class; /** * The two possible authentication types for a resource. */ enum AuthenticationType { CONTAINER, APPLICATION } /** * The authentication type to use for this resource. * This may be specified for resources representing a * connection factory of any supported type, and must * not be specified for resources of other types. */ AuthenticationType authenticationType() default AuthenticationType.CONTAINER; /** * Indicates whether this resource can be shared between * this component and other components. * This may be specified for resources representing a * connection factory of any supported type, and must * not be specified for resources of other types. */ boolean shareable() default true; /** * A product specific name that this resource should be mapped to. * The name of this resource, as defined by the <code>name</code> * element or defaulted, is a name that is local to the application * component using the resource. (It's a name in the JNDI * <code>java:comp/env</code> namespace.) Many application servers * provide a way to map these local names to names of resources * known to the application server. This mapped name is often a * <i>global</i> JNDI name, but may be a name of any form. <p> * * Application servers are not required to support any particular * form or type of mapped name, nor the ability to use mapped names. * The mapped name is product-dependent and often installation-dependent. * No use of a mapped name is portable. */ String mappedName() default ""; /** * Description of this resource. The description is expected * to be in the default language of the system on which the * application is deployed. The description can be presented * to the Deployer to help in choosing the correct resource. */ String description() default ""; } -
其中最重要的,也是最常用到的是属性
name,以及属性type -
属性
name用来指定注入的Bean名称,属性type用来指定注入的Bean的类型(注意假设我们待注入实现了某个接口的对象,这个通过type可以是指定某个特定的子类)
2.1 name + type
-
执行流程如下

-
特别注意这个
type,是用来指明待注入Bean的类型的,通常来说是指待注入对象的子类-
举个例子
-
假设现在有接口
MyService,并有3个子实现类MyServiceImplA,MyServiceImplB,MyServiceImplC
-
正常来说我们在
Controller层使用注解@Resource注入对象时是这样写的
-
也就是带注入类型写的是某个接口,而不是某个特指的子类(方便后面维护,扩展)
-
而我们使用属性
type就可指明我们想要的是某个特定的子实现类
-
当然,指明类型为
MyServiceImplB,实际上注入的Bean可以是MyServiceImplB的子类,不仅仅局限于MyServiceImplB
-
-
一点想法
- 待注入对象的类型我们往往写的是某个接口
- 其实这是类似于我们常写的
List list = new ArrayList<Integer>()的写法 - 这是一种多态的表现,即父类变量可以引用子类实现
- 也是面向接口编程的写法
- 这样子最大的好处在于比如说后期我们需要把
ArrayList改为LinkedList,直接改动一行代码即可(也就是改new就行了),后面代码全不用动 - 但是如果说一开始写的是
ArrayList list = new ArrayList<Integer>() - 在后续业务代码中可能会多多少少用到
ArrayList独有的方法 - 那么在后续的改动集合类型时
- 要改动的代码可能就会非常大
- 所以我们在写待注入对象的类型时,也写的是某个接口
- 然后可以通过注解
@Resource的属性type来特别指明其实现类(不同实现类可能带有不同的特性)
2.2 name
-
执行流程如下

-
特别注意,此时的
type默认就是待注入对象的类型 -
如果说有
Bean的name同名,但是类型不符合待注入对象的类型的话,一样会注入失败,直接抛异常
2.3 type
-
执行流程如下

-
特别注意如果此时寻找到了多个符合类型
Bean的情况 -
这时会默认把待注入对象的变量名称作为
Bean名称,再次去匹配的-
举个例子
-
假设现在接口
MyService的子实现类还是有三个,分别是MyServiceImplA,MyServiceImplB,MyServiceImplC,并且都注入到了IOC容器中 -
Controller层如下
-
可以看到符合
MyService类型的有三个Bean(myServiceImplA,myServiceImplB,myServiceImplC),所以Spring此时会将待注入对象的名称,也就是myServiceImplB,作为Bean的名称再次去匹配,从而会把MyServiceImplB类型的Bean注入进来
-
2.4 无name也无type
-
执行流程如下

-
特别注意注解
@Resource在无配置属性name,也无配置属性type时,此时可以理解为会将待注入对象的类型认为是属性type,待注入对象的变量名称认为是属性name -
然后按照上图的执行流程走(注意按
type和name寻找不到唯一匹配的Bean时,此时还会多出一步按照type寻找的过程)-
举个例子
-
假设现在接口
MyService的子实现类还是有三个,分别是MyServiceImplA,MyServiceImplB,MyServiceImplC,并且都注入到了IOC容器中 -
Controller层配置如下
-
此时会按照
type=MyService,name=myServiceImplA到IOC容器中寻找唯一Bean -
刚好找到了,这时就会把
MyServiceImplA对应的Bean注入进来 -
-----------------------------俺是分割线----------------------------------
-
又比如另一个例子
-
假设现在接口
MyService的子实现类还是有三个,分别是MyServiceImplA,MyServiceImplB,MyServiceImplC,但是只有MyServiceImplB注入到了IOC容器中 -
Controller层配置如下
-
一开始就会按照
type=MyService,name=tem到IOC容器中寻找唯一Bean -
但是找不到,于是开始走下一步
-
下一步就是按照
type=MyService到IOC容器中寻找Bean -
此时刚好找到了
MyServcieImplB,所以就把对应的Bean注入了进来 -
但是如果说一开始
MyServiceImplA,MyServiceImplB,MyServiceImplC都注入到了IOC容器,此时就会抛异常
-
3. 一些点
-
特别说明下
-
注解
@Autowired来自Spring框架
-
注解
@Resource来自Java(JSR-250)
-
-
两者的作用点也是不同的,源码上都写有
-
@Autowired:可以标记在构造器上,方法上,方法参数上,成员变量上,注解上
-
@Resource:可以标记在类上,成员变量上,方法上
-
4. 引用
- 苏三说技术-SpringBoot中,@Autowired和@Resource使用起来到底有什么区别?
- @Autowired和@Resource区别
- 一堆土豆33-@Autowired和@Resource到底有什么区别