- 联系方式: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到底有什么区别