这个问题困扰了我好久,一直疑问这个接口的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的容器中查找类型是TestService的bean,刚好找到有且只有一个此类型的bean,即testServiceImpl,所以就把testServiceImpl自动装配到了controller的实例testService中,testService其实就是TestServiceImpl实现类;

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

注:

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种方式来指定具体要使用哪一个实现:

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的名字,就会默认获取TestServiceImpl2的bean,如果指定了bean的名字则以指定的为准。

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

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

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

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

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

  1. Spring容器中获取bean实例的方法

    // 得到上下文环境 WebApplicationContext webContext = ContextLoader .getCurrentWebApplicationContext(); // 使 ...

  2. spring读取配置文件,且获取bean实例

    import org.springframework.beans.factory.BeanFactory;import org.springframework.beans.factory.xml.Xm ...

  3. spring-如何在项目启动的情况下获取Bean实例

    十年阿里,就只剩下这套Java开发体系了 >>>   大家都知道,项目启动的时候,spring读取xml文件,将配置的bean 或者 注解下的controller service d ...

  4. Spring常用注解,自动扫描装配Bean

    1 引入context命名空间(在Spring的配置文件中),配置文件如下: xmlns:context="http://www.springframework.org/schema/con ...

  5. spring使用注解的方式创建bean ,将组件加入容器中

    第一种使用@Bean的方式 1.创建一个bean package com.springbean; public class Person { private String name; private ...

  6. Spring(3.2.3) - Beans(3): Bean 实例的创建方式

    创建一个 Bean 实例对象的方法通常有如下方式: 调用构造器创建 Bean 实例 调用静态工厂方法创建 Bean 实例 调用实例工厂方法创建 Bean 实例 使用构造器创建 Bean 实例 XML ...

  7. spring获取bean 实例

    ApplicationContext ctx = new ClassPathXmlApplication("applicationContext.xml"); DataSource ...

  8. spring autoWire注解和@resource注解区别

    1.autoWire注解主要是按类型匹配.因为autowire的扫描机制,是按照接口类型来扫描bean的. 而JSR250 @resource注解是通过名称扫描注入的. @autowire注解的扫描方 ...

  9. spring自动注解Autowired配置

    1.spring注解:http://blog.csdn.net/xyh820/article/details/7303330/ 2.最简ssm配置:http://blog.csdn.net/qq_18 ...

随机推荐

  1. VS 点击页面自动定位到解决方案资源管理器目录位置

    点击工具→选项→项目和解决方案:勾上“在解决方案资源管理器中跟踪活动项”.

  2. CORS跨域带来的preflight request

    CORS跨域带来的preflight request https://blog.csdn.net/baidu_35407267/article/details/79043515 HTTPS://blo ...

  3. visual studio 2019 error MSB3073 exited with code 1

    在用vs2019编译C++工程时,出现错误. 原因是设置的命令没有运行成功,需要将这条命令关闭.不编译就行了. 解决方法,打开property manager,打开property pages,将其中 ...

  4. 查找算法(3)--Interpolation search--插值查找

    1. 插值查找 (1)说明 在介绍插值查找之前,首先考虑一个新问题,为什么上述算法一定要是折半,而不是折四分之一或者折更多呢? 打个比方,在英文字典里面查“apple”,你下意识翻开字典是翻前面的书页 ...

  5. es查询示例

    1. 建立连接 from elasticsearch import Elasticsearch es = Elasticsearch(["localhost:9200"]) 2. ...

  6. php 回调函数结合闭包(匿名函数)的使用示例

    <?php /** * php 回调函数结合闭包(匿名函数)的使用 */ function callback( $callback ){ $variable = 'program'; $ret1 ...

  7. [LeetCode] 768. Max Chunks To Make Sorted II 可排序的最大块数 II

    This question is the same as "Max Chunks to Make Sorted" except the integers of the given ...

  8. 容器服务如何在企业客户落地?Rancher 解决之道分享

    Docker 的优势和趋势我想不必再赘述,那么对于非互联网公司的传统企业客户,以及我们大量的围绕企业客户做集成.交付解决方案的服务提供商,需要考虑的一个问题就是怎么样把容器技术以高质量.低成本.易维护 ...

  9. [数据结构 - 第3章] 线性表之顺序表(C++实现)

    一.类定义 顺序表类的定义如下: #ifndef SEQLIST_H #define SEQLIST_H typedef int ElemType; /* "ElemType类型根据实际情况 ...

  10. python及Django的json序列化

    JSON是一种文本序列化格式(它输出的是unicode文件,大多数时候会被编码为utf-8),人们很容易进行阅读和编写.python自带的dumps方法很有用,能很容易将字典dict类型数据转化为js ...