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