每一个优秀的人,都有一段沉默的时光。 那段时光,是付出了不少努力,却得不到结果的日子,咱们把它叫做扎根。面试
这个问题困扰了我很久,一直疑问这个接口的bean是怎么注入进去的?由于只看到使用@Service注入了实现类serviceImpl,使用时怎么能获取的接口,并且还能调用到实现类的方法,难道这个接口是在何时自动注入了进去,且和实现类关联上了?spring
接口微信
public interface TestService { public String test(); }
实现类implapp
@Service public class TestServiceImpl implements TestService { @Override public String test() { return "TestServiceImpl"; } }
Controller的调用:ide
@RestController public class TestCtl { @Autowired private TestService testService; @RequestMapping("/test") public String test() { return testService.test(); } }
请求结果:spa
后来才知道,并无注入接口的bean,只注入了实现类serviceImpl的bean,接口只是用来接收的。code
这里就要说到@Autowired/@Resource的注入原理了:@Autowired是Spring的注解,Autowired默认先按byType,若是发现找到多个bean,则,又按照byName方式比对,若是还有多个,则报出异常;@Resource 是JDK1.6支持的注解,默认按照名称(Byname)进行装配, 若是没有指定name属性,当注解写在字段上时,默认取字段名,按照名称查找,若是注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。可是须要注意的是,若是name属性一旦指定,就只会按照名称进行装配。blog
再来讲Controller获取实例的过程:使用@Autowired,程序在spring的容器中查找类型是TestService的bean,恰好找到有且只有一个此类型的bean,即testServiceImpl,因此就把testServiceImpl自动装配到了controller的实例testService中,testService其实就是TestServiceImpl实现类;继承
若是使用的是@Resource,则是先在容器中查找名字为testService的bean,但并无找到,由于容器中的bean名字是TestServiceImpl(若是@Service没指定bean的value属性,则注入bean的名字就是类名,若是指定了则是指定的名字),而后再经过类型查找TestService类型的bean,找到惟一的了个TestService类型bean(即TestServiceImpl),因此就自动装配实例成功了。更多面试题,欢迎关注公众号Java面试题精选接口
byName 经过参数名 自动装配,若是一个bean的name 和另一个bean的 property 相同,就自动装配。
byType 经过参数的数据类型自动自动装配,若是一个bean的数据类型和另一个bean的property属性的数据类型兼容,就自动装配
效率上来讲@Autowired/@Resource差很少,不过推荐使用@Resource一点,由于当接口有多个实现时@Resource直接就能经过name属性来指定实现类,而@Autowired还要结合@Qualifier注解来使用,且@Resource是jdk的注释,可与Spring解耦。
若是一个接口有多个实现类时,经过注解获取实例时怎么知道应该获取的是哪个实现类serviceImpl呢?
再增长了一个实现类TestServiceImpl2
@Service public class TestServiceImpl2 implements TestService { @Override public String test() { return "TestServiceImpl2"; } }
多个实现类的话可经过如下2种方式来指定具体要使用哪个实现:
@Autowired 须要结合@Qualifier来使用,以下:
@Autowired @Qualifier("testServiceImpl") private TestService testService;
@Resource可直接经过指定name属性的值便可,不过也能够使用@Qualifier(有点画蛇添足了…)
@Resource(name = "testServiceImpl") private TestService testService;
@Resource若是不显示的指定name值,就会自动把实例变量的名称做为name的值的,因此也能够直接这样写:
@Resource private TestService testServiceImpl;
@Service @Primary public class TestServiceImpl2 implements TestService { @Override public String test() { return "TestServiceImpl2"; } }
这样若是在使用@Autowired/@Resource获取实例时若是不指定bean的名字,就会默认获取TestServiceImpl2的bean,若是指定了bean的名字则以指定的为准。
为何非要调用接口来画蛇添足,而不直接调用实现类serviceImpl的bean来得简单明了呢?
一、 直接获取实现类serviceImpl的bean也是能够的;
二、 至于加一层接口的缘由:一是AOP程序设置思想指导,给别人调用的接口,调用者只想知道方法和功能,而对于这个方法内部逻辑怎么实现的并不关心;二是能够下降各个模块间的关联,实现松耦合、程序分层、高扩展性,使程序更加灵活,他除了在规范上有卓越贡献外,最精髓的是在多态上的运用;继承只能单一继承,接口却能够多实现
三、 当业务逻辑简单,变动较少,项目自用时,省略掉接口直接使用实现类更简单明了;反之则推荐使用接口;