Spring通过注解@Autowired/@Resource获取bean实例时为什么可以直接获取接口而不是注入的类

问:

这个问题困扰了我好久,一直疑问这个接口的bean是怎么注入进去的?因为只看到使用 @Service 注入了实现类 serviceImpl,使用时怎么却获取的接口,而且还能调用到实现类的方法,难道这个接口是在什么时候自动注入了进去,且和实现类关联上了?

接口
public interface TestService {
    public String test();
}
实现类impl
@Service
public class TestServiceImpl implements TestService{
    @Override
    public String test() {
        return "TestServiceImpl";
    }
}
Controller的调用:
@RestController
public class TestCtl {
    @Autowired
    private TestService testService;
    @RequestMapping("/test")
    public String test() {
        return testService.test();
    }
}
请求结果:

在这里插入图片描述

答:

后来才知道,并没有注入接口的bean,只注入了实现类serviceImpl的bean,接口只是用来接收的,这里就要说到 @Autowired/@Resource的注入原理了:@Autowired是Spring的注解,Autowired默认先按 byType,如果发现找到多个 bean,则,又按照 byName方式比对,如果还有多个,则报出异常;@Resource 是JDK1.6支持的注解,默认按照名称( Byname)进行装配, 如果没有指定 name属性,当注解写在字段上时,默认取字段名,按照名称查找,如果注解写在 setter方法上默认取属性名进行装配。当找不到与名称匹配的 bean时才按照类型进行装配。但是需要注意的是,如果 name属性一旦指定,就只会按照名称进行装配。

再来说Controller获取实例的过程:使用 @Autowired,程序在 spring的容器中查找类型是 TestServicebean,刚好找到有且只有一个此类型的 bean,即 testServiceImpl,所以就把 testServiceImpl自动装配到了 controller的实例 testService中, testService其实就是 TestServiceImpl实现类;

如果使用的是 @Resource,则是先在容器中查找名字为 testServicebean,但并没有找到,因为容器中的 bean名字是 TestServiceImpl(如果 @Service没指定 beanvalue属性,则注入 bean的名字就是类名,如果指定了则是指定的名字),然后再通过类型查找 TestService类型的 bean,找到唯一的了个 TestService类型 bean(即 TestServiceImpl),所以就自动装配实例成功了。

注:

byName 通过参数名 自动装配,如果一个 beanname 和另外一个 beanproperty 相同,就自动装配。

byType 通过参数的数据类型自动自动装配,如果一个 bean的数据类型和另外一个 beanproperty属性的数据类型兼容,就自动装配

效率上来说 @Autowired/@Resource差不多,不过推荐使用 @Resource一点,因为当接口有多个实现时 @Resource直接就能通过 name属性来指定实现类,而 @Autowired还要结合 @Qualifier注解来使用,且 @Resourcejdk的注释,可与 Spring解耦。

问:

如果一个接口有多个实现类时,通过注解获取实例时怎么知道应该获取的是哪一个实现类serviceImpl呢?

再增加了一个实现类TestServiceImpl2
@Service
public class TestServiceImpl2 implements TestService{
    @Override
    public String test() {
        return "TestServiceImpl2";
    }
}

答:

多个实现类的话可通过以下2种方式来指定具体要使用哪一个实现:

1、 通过指定bean的名字来明确到底要实例哪一个类

@Autowired 需要结合 @Qualifier来使用,如下:

	@Autowired
    @Qualifier("testServiceImpl")
    private TestService testService;

@Resource可直接通过指定name属性的值即可,不过也可以使用**@Qualifier**(有点多此一举了…)

	@Resource(name = "testServiceImpl")
    private TestService testService;  

@Resource如果不显示的指定name值,就会自动把实例变量的名称作为name的值的,所以也可以直接这样写:

	@Resource
    private TestService testServiceImpl;
2、 通过在实现类上添加@Primary注解来指定默认加载类
@Service
@Primary
public class TestServiceImpl2 implements TestService{
    @Override
    public String test() {
        return "TestServiceImpl2";
    }
}

这样如果在使用 @Autowired/@Resource获取实例时如果不指定bean的名字,就会默认获取TestServiceImpl2bean,如果指定了bean的名字则以指定的为准。

问:

为什么非要调用接口来多此一举,而不直接调用实现类serviceImpl的bean来得简单明了呢?

答:

1、 直接获取实现类serviceImplbean也是可以的;

2、 至于加一层接口的原因:一是AOP程序设置思想指导,给别人调用的接口,调用者只想知道方法和功能,而对于这个方法内部逻辑怎么实现的并不关心;二是可以降低各个模块间的关联,实现松耦合、程序分层、高扩展性,使程序更加灵活,他除了在规范上有卓越贡献外,最精髓的是在多态上的运用;继承只能单一继承,接口却可以多实现

3、 当业务逻辑简单,变更较少,项目自用时,省略掉接口直接使用实现类更简单明了;反之则推荐使用接口;

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值