spring自动注入接口的多个实现类(结合策略设计模式)

在使用spring开发的时候,有时候会出现一个接口多个实现类的情况,但是有没有有时候有这样一种情况,就是你的逻辑代码里面还不知道你需要使用哪个实现类,就是比如说:你去按摩,按摩店里面有几种会员打折,比如有,vip、svip和普通用户,那按摩店里面是不是需要对这些会员定义好不同的折扣,然后根据每个用户不同的会员计算出不同的消费情况

虽然这样的情况,对于一般人来说,第一眼肯定就是说,直接加 if else 去判断就可以了

这样做,对于实现功能而言,肯定是没问题,如果以后这个按摩店又增加一种会员,那你是不是又要去修改你的逻辑代码去在加一个 if else ,这样就违反了系统架构设计的开闭原则,这样写if else  也使你的代码看起来不优雅。

所以在代码里面,我们可以先定义一个DiscountStrategy接口类

public interface DiscountStrategy {

    public String getType();
    public double disCount(double fee);

}

然后在写他的几个实现类

普通用户实现类

@Service
public class NormalDisCountService implements  DiscountStrategy {


    public String getType(){
        return "normal";
    }

    public double disCount(double fee){
        return fee * 1;
    }

}

会员实现类

public class VipDisCountService  implements  DiscountStrategy{


    public String getType(){
        return "vip";
    }

    public do
<think>嗯,用户想理解Spring自动注入时为什么注入的是实现类而非接口。这是个关于Spring依赖注入机制的问题,需要从IoC容器的工作原理和动态代理机制来解释。用户可能正在学习Spring框架,遇到了自动注入的困惑。从问题表述看,用户应该已经了解基本概念,但需要更深入的技术解释。这类问题通常涉及三个核心点:容器管理机制、代理模式差异和类型匹配规则。首先需要明确的是,Spring容器实际存储的是BeanDefinition对应的实例对象。当用户定义@Service时,容器创建的是实现类实例(如UserServiceImpl),但依赖注入时存在两种处理方式:1.如果注入点声明为接口类型(如IUserService),Spring会通过getBean(Interface.class)查找匹配的实现类。这里有个关键点:当接口只有一个实现类时,容器能直接关联;当有多个时需要用@Qualifier指定2.但实际注入对象类型取决于是否启用AOP。未启用AOP时注入原生实现类实例;启用后则注入代理对象关于代理机制的影响特别重要。Spring默认对接口使用JDK动态代理,会生成实现相同接口的代理类(如$Proxy123)。此时:-代理类与实现类是兄弟关系而非父子-只能用接口类型接收代理对象-若尝试用实现类接收会因类型不匹配报错用户提到的"注入实现类"可能观察到两种情况:1.在无AOP场景,@AutowiredprivateUserServiceImpl能成功注入,因为容器确有该类型实例2.但在有AOP时,容器实际返回代理对象(类型为$Proxy123),无法强制转换为UserServiceImpl可以通过实验验证:```java@AutowiredprivateIUserServiceservice;//成功:代理对象实现IUserService@AutowiredprivateUserServiceImplserviceImpl;//可能失败:代理对象并非UserServiceImpl类型```因此准确来说:Spring容器始终管理实现类实例,但注入时返回的是:-原生对象(无代理时)-代理对象(有AOP时),且代理对象只能通过接口访问用户可能混淆了"容器存储的对象类型"和"依赖注入时的对象类型"。这个理解偏差很常见,需要强调容器内部持有实现类实例,但暴露给使用者的可能是代理对象。准备用弹簧和插头的比喻来说明:容器像配电箱(内部是具体电线实现),但给使用者提供的是标准插座接口。当需要电路保护(AOP)时,会在中间加装保护器(代理),但保护器仍然符合插座接口规范。相关问题设计方向:1.技术验证:如何查看实际注入类型(debug输出getClass())2.解决方案:多实现类时的处理方案3.原理延伸:CGLIB代理如何改变这个规则4.错误排查:ClassCastException的解决方法</think>### Spring自动注入机制解析 Spring框架在自动注入时**实际注入的是接口实现类对象**,但开发者通常声明接口类型接收。这种设计源于Spring依赖注入原理和代理机制: #### 核心机制 1. **容器管理实现类** Spring容器实际创建和管理的是`@Service`、`@Component`等注解标记的实现类对象(如`UserServiceImpl`),而非接口本身。 2. **依赖查找规则** 当注入点声明为接口类型时: ```java @Autowired private IUserService userService; // 声明接口类型 ``` Spring会通过**byType**方式查找匹配的实现类: - ✅ 若接口**只有一个实现类**,自动注入实现类对象 - ❗ 若接口有**多个实现类**,需配合`@Qualifier`指定具体实现: ```java @Autowired @Qualifier("userServiceImpl") // 指定Bean名称 private IUserService userService; ``` #### 为何不直接注入实现类? 3. **动态代理限制** Spring AOP默认使用**JDK动态代理**(基于接口): - 代理对象实现与原类相同的接口 - 代理对象与原实现类是**兄弟关系**而非父子关系 ```mermaid graph LR A[IUserService接口] --> B[UserServiceImpl] A --> C[JDK动态代理对象] B & C -.同级.-> D{不能互相转换} ``` - 若注入点声明为实现类类型: ```java @Autowired private UserServiceImpl userService; // 声明具体实现类 ``` **将无法接收代理对象**,导致注入失败或AOP失效[^3]。 4. **松耦合设计** 面向接口编程符合设计原则: - 业务层无需关注具体实现 - 方便替换实现方案(如测试时注入Mock实现) #### 验证实验 当接口多个实现类时,Spring会将其注入集合: ```java // 接口 public interface IMqttHandler<T> {} // 实现类1 @Service public class MqttHandlerImpl1 implements IMqttHandler {} // 实现类2 @Service public class MqttHandlerImpl2 implements IMqttHandler {} // 自动注入集合 @Autowired private List<IMqttHandler> handlers; // 包含两个实现类对象[^5] ``` #### 最佳实践 | 场景 | 注入方式 | 示例 | |------|----------|------| | 单一实现 | 接口声明 | `@Autowired IUserService service` | | 多实现类 | `@Qualifier` | `@Qualifier("specificImpl")` | | 获取所有实现 | 集合注入 | `List<IMqttHandler> handlers` | > **关键结论**:Spring容器实际存储的是实现类对象,但注入时需通过接口类型接收以兼容AOP代理机制。这是设计层面的约束而非技术限制[^3][^4]。 --- ### 相关问题 1. **Spring AOP使用JDK动态代理还是CGLIB?如何强制指定代理方式?** 2. **当接口多个实现类时,除了`@Qualifier`还有哪些注入方案?** 3. **如何验证Spring容器中某个接口的实际注入实现类?** 4. **为什么在单元测试中直接`new`实现类对象会导致AOP失效?** 5. **Spring Boot中`@Primary`注解如何解决多实现类注入冲突?** [^1]: Spring注入接口实现类机制解析 [^2]: 单一实现类自动注入原理 [^3]: JDK动态代理对注入类型的限制 [^4]: 多实现类注入解决方案 [^5]: 多实现类集合注入验证
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风某人~Wind

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值