前言:
使用Spring系列的框架对这三个注解肯定都不会陌生,这三个注解有一个特性,就是用于属性注入,说白了点就是将Spring容器中的对象取出来,这样我们才可以使用,那么这三者到底是什么关系,又有什么区别呢?
一.三个注解
若是不想深入了解,看这一段就够了:其实我们可以简单的这么看Autowired+Qualifier=Resource,这就是他们三者的关系,Autowired根据类型找实现类,一个接口有多个实现类时需要通过Qualifier来指明需要哪个实现类,这是就需要Autowired+Qualifier一起使用才可以。Resource则是不声明名称时按照类型查找效果与Autowired相同,声明名称时就等于Autowired+Qualifier的组合。下面我们就来详细的看一下这三个注解。
1.Autowired注解
相信大部分人在属性注入时使用的都是Autowired或者Resource注解,那么Autowired是怎么进行属性注入的呢?其实Autowired是根据接口的类型进行注入的,有如下代码
@Autowired SupplierService supplierService; @RequestMapping("test") public void test(){ supplierService.deleteByOid("2"); }
我们使用Autowired注解后,Spring就会自动去容器中寻找SupplierService的实现类,进行注入进来。
1.问题一:怎么将对象的实例放入到Spring容器中呢?
自然就是我们常用的compoment、controller、service、repository这些注解了,这些注解都可以将类的实例放入到Spring容器中,且默认都是单利模式。然后我们可以使用Autowired进行依赖注入。
2.问题二:如果依赖注入的接口有多个实现类呢?
我们已经知道Autowired是根据接口的类型自动找到其实现类然后实现依赖注入,那么如果接口有多个实现类该怎么办?依赖注入的又是谁呢,这时就要说到Qualifier注解了。
2.Qualifier注解
上面已经说了,当一个接口有多个实现类时,Spring容器按照类型注入就会有一定问题,注入的可能就不是我们想要的类型了。比如下方代码:
//这是接口 public interface SupplierService { void deleteByOid(String oid); } //这是第一个实现类 @Service public class SupplierServiceImplOne implements SupplierService{ @Override public void deleteByOid(String oid) { System.out.println("One...我被执行了"); } } //这是第二个实现类 @Service public class SupplierServiceImplTwo implements SupplierService{ @Override public void deleteByOid(String oid) { System.out.println("Two...我被执行了"); } }
一个接口有两个实现类,如果我们还是这么写(如下),那到底注入的是哪个呢?
@Controller public class Test { @Autowired SupplierService supplierService; @RequestMapping("test") public void test(){ supplierService.deleteByOid("2"); } }
事实上Spring在之前的版本中运行以上代码,不指定具体的实现类是会报错,具体哪个版本修改了不得而知,现在是有个默认模式会去执行其中一个实现类。那么如果执行的不是我们想要的实现类怎么办呢?就需要Qualifier注解了,该注解可以实现根据实现类的名称进行注入。如下:
@Controller public class Test { @Autowired @Qualifier("supplierServiceImplTwo") SupplierService supplierService; @RequestMapping("test") public void test(){ supplierService.deleteByOid("2"); } }
这样我们就可以在多个实现类的情况下实现准确注入了。
注意:Qualifier注解的值是小写字母开头的接口实现类的名称。这个是接口实现类的默认名称,我们可以使用@Service(“supplierService”)时显示的指定实现类的名称,然后使用Qualifier注解时进行使用,如果不指定,默认的就是接口实现类的小写字母开头的类名,就像上方代码那样。
1.问题一:只使用Qualifier可以吗?
答案是不可以的,使用Qualifier是在使用Autowired的基础上才有意义的。如果只使用Qualifier,那么Spring是不清楚你要注入什么类型的,Autowired告诉Spring我要什么类型,Qualifier告诉Spring我要这个名称的实现类,他俩配合完成了一次属性注入。
2.问题二:一个属性注入就需要两个注解,是不是太麻烦?
都说世界是懒人推动的。。。,懒人就问了,我一个属性注入就要两个注解?也太麻烦了,所以又有一个Resource注解来取代在多实现类的场景下必须多个注解才可完成的工作(不过Resource并不是Spring的注解)。
3.Resource注解
上面已经说了Resource等于Autowired+Qualifier注解,如果我们不给Resource指定name属性时,那么Resource是按照类型注入的,此时如果接口有多个实现类,就会报错,笔者记得之前一个接口多个实现类使用Autowired也是报这个错,如下
nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException
但是不知何时Spring修复了这一点,所以也就是说,使用Resource不声明name属性时其和Autowired是一样的效果都是按照类型进行注入,但是如果一个接口有多个实现类使用Resource是编译是过不去,但是使用Autowired还是可以编译过去。如果使用Resource并声明他的name属性那么就可以正常编译了,这个效果就等同于Autowired加Qualifier了。如下:
// @Autowired // @Qualifier("supplierServiceImplTwo") @Resource(name ="supplierServiceImplTwo")
如上所示,注释掉的部分与Resource是相同的作用,所以我们说Resource就是为了取代Autowired与Qualifier的,加上名称后,就会根据名称去Spring容器中获取对象。这个名称前面也说了,并不是类名,如果在往Spring容器中注入对象时不显示声明,默认就是注入对象所属类的小写字母开头的类名。
二.总结
知道了这三个注解的区别,那么我们应该怎么用更合理,更不容易出错呢,在一个接口仅有一个实现类的场景中,我们使用Autowired和Resource其实效果一致,在一个接口多个实现类的场景。这时我们使用Resource更简便快捷,仅需要为Resource指明name属性即可准确进行属性注入了,以上就是关于Aotowired、Qualifier、Resource三个注解的总结了,总结是为了让自己记忆更清楚,如果能帮到路过的你就更好了。