我知道在Spring中,能够用@autowired @resource @Inject三种方式进行依赖注入,三种方式的区别在什么地方呢?java
若是没有兴趣能够直接跳到下面的结论。spring
若是不知道如何测试Spring项目的能够点击这里。ide
首先咱们定义一个Person接口:测试
package site.abely.service; public interface Person { void sayHello(); }
而后定义两个实现类Chinese,Americanui
package site.abely.service; import org.springframework.stereotype.Component; @Component public class Chinese implements Person { @Override public void sayHello() { System.out.println("你好"); } }
package site.abely.service; import org.springframework.stereotype.Component; @Component public class American implements Person { @Override public void sayHello() { System.out.println("Hello"); } }
先按类型注入,而后按照名字注入,都没法找到惟一的一个实现类出错。.net
咱们将American类中@Component注释,这样在Spring环境中只有Chinese一个实现类,测试代码以下:指针
@Autowired Person person; @Test public void testSay(){ person.sayHello(); }
输出你好
。code
由于只有Chinese实现了Person,因此会正确运行(经过类型查找)。对象
咱们取消American类中@Component的注释,运行程序会出现异常:接口
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'site.abely.service.Person' available: expected single matching bean but found 2: american,chinese
由于有American和Chinese两个实现类,Spring不知道该用哪个注入。
修改测试代码:
@Resource Person chinese; @Test public void testSay(){ chinese.sayHello(); }
结果会输出你好
。此时变量名chinese等于Chinese默认的Qualifier名字。
若是只有一个实现类Chinese(删除American类),并且Chinese不实现Person接口,此时怎么注入Person chinese都会出错(请求的是Person对象,注入的却不是)。
@Autowired(required=false)中若是设置required为false(默认为true),则注入失败时不会抛出异常,但person.sayHello();
调用时会出现空指针异常。
在Spring的环境下,@Inject和@Autowired是相同的,都是使用AutowiredAnnotationBeanPostProcessor来处理依赖注入,@Inject是jsr-330定义的规范,仍是比较推荐使用这种方式进行依赖注入,若是使用这种方式,切换到Guice也是能够的。
若是硬要说两个的区别,首先@Inject是Java EE包里的,在SE环境须要单独引入。另外一个区别在于@Autowired能够设置required=false而@Inject并无这个设置选项。
先按名字注入,在按类型注入,都没法找到惟一的一个出现异常。
这是jsr-250定义的规范,相对于jsr-330算是比较老的了。这里不推荐使用这种注入方式,下面讨论一下其注入的问题。
首先咱们注释American里的@Component,这样在Spring托管的Bean里只有Chinese实现了Person接口,测试用例以下:
@Resource Person person; @Test public void testSay(){ person.sayHello(); }
输出结果:你好
。 此时@Resource先按名字person,并未找到person的bean,而后按照类型来找,只有Chinese,注入成功。
取消American中的@Component注释,出现以下异常:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'site.abely.service.Person' available: expected single matching bean but found 2: american,chinese
此时不管经过名字没法肯定,而后经过类型仍是没法肯定,抛出异常。
修改测试代码
@Resource Person chinese; @Test public void testSay(){ chinese.sayHello(); }
输出结果你好
,此时按名字找到了chinese。
咱们上面也说了,咱们推荐是用@Inject,不会与Spring产生耦合,固然若是有必要也可使用@Autowired,为何不推荐使用@Resouce呢?
如今咱们让Chinese不实现Person接口,但仍然被Spring管理,测试代码以下:
@Resource Person chinese; @Test public void testSay(){ chinese.sayHello(); }
结果以下:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'site.abely.service.TestTest': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'chinese' is expected to be of type 'site.abely.service.Person' but was actually of type 'site.abely.service.Chinese'
结果的问题在于,American是Person的惟一实现类并且被Spring托管,此时却不会被注入。
使用@Autowired输出Hello
对比你能够发现@Resource的问题所在,也许你认为@Resouce的结果是合理的,可是你要考虑到@Qualifier或@Named的做用,咱们能够用以下代码取得和@Resouce相似的效果:
@Autowired @Qualifier("chinese") Person chinese; @Test public void testSay() { chinese.sayHello(); }
出现异常:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'site.abely.service.Person' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=chinese)}
这样也会去找一个bean id为chinese的bean,在这种状况下(只有一个实现类),@Resouce要想得到正确的注入只有一个方式,正确的命名Person american
或Person person
(person能成功的缘由在于Spring中没有bean的id为person的托管类),若是命名不正确,即便使用@Qualifier("american")
也不会正确注入(@Resouce不会鸟它的),这会给开发者带来额外的负担,即便只有一个实现类,@Resouce也有可能没法注入。这也是开发中常见的事情,若是你的命名和Sping中某个bean的id相同,@Resouce会出现一些意想不到的问题。