spring中直接对接口进行@Autowired自动注入情况分析

1.单实现类注入(最常见情况)

  • 条件:接口只有一个实现类(被 Spring 管理)
  • 结果:自动注入成功
// 接口
public interface PaymentService {
    void processPayment();
}

// 唯一实现
@Service
public class CreditCardService implements PaymentService {
    @Override
    public void processPayment() {
        // 信用卡支付逻辑
    }
}

// 注入点
@Controller
public class CheckoutController {
    @Autowired
    private PaymentService paymentService; // 注入 CreditCardService 实例
}

当使用 @Autowired 注入接口时:
Spring 会在容器中查找 实现了该接口的 Bean
如果找到唯一实现,则注入该实现类的实例
如果有多个实现,需要配合 @Qualifier 指定具体实现

  • Spring 的注入流程
步骤说明
1. 组件扫描@ComponentScan(“com.fjd”) 扫描到 AccountServiceImpl
2. Bean 注册将 AccountServiceImpl 实例注册为 Bean
3. 接口关联 Spring知道它实现了 AccountService 接口
4. 自动注入当遇到 @Autowired AccountService 时,注入实现类实例

2. 多实现类: 使用 @Primary 指定主实现

  • 条件:多个实现类,其中一个标记 @Primary
  • 结果:注入被标记为 @Primary 的实现
@Service
public class PayPalService implements PaymentService {
    // 实现1
}

@Service
@Primary // 标记为首选实现
public class StripeService implements PaymentService {
    // 实现2
}

// 注入点
@Controller
public class CheckoutController {
    @Autowired
    private PaymentService paymentService; // 注入 StripeService 实例
}

3. 多实现类: 使用 @Qualifier 精确指定

  • 条件:多个实现类,使用 @Qualifier 指定 Bean 名称
  • 结果:注入指定名称的实现
@Service("paypalService")
public class PayPalServiceImpl implements PaymentService {
    // 实现1
}

@Service("stripeService")
public class StripeServiceImpl implements PaymentService {
    // 实现2
}

// 注入点
@Controller
public class CheckoutController {
    @Autowired
    @Qualifier("stripeService") // 按名称指定
    private PaymentService paymentService; // 注入 StripeServiceImpl
}

4. 多实现类: 字段名匹配 Bean 名称

  • 条件:字段名与某个实现类的 Bean 名称一致
  • 结果:注入名称匹配的实现
@Service // 默认 Bean 名称为 "payPalService"
public class PayPalService implements PaymentService {
    // 实现1
}

@Service // 默认 Bean 名称为 "stripeService"
public class StripeService implements PaymentService {
    // 实现2
}

// 注入点
@Controller
public class CheckoutController {
    @Autowired
    private PaymentService stripeService; // 注入 StripeService
}

5. 动态代理接口注入(MyBatis/RPC 等)

  • 条件:接口由框架动态生成实现(非手动编写)
  • 结果:注入动态代理对象
// MyBatis Mapper 接口(无实现类)
public interface UserMapper {
    @Select("SELECT * FROM users WHERE id = #{id}")
    User findById(Long id);
}

// 注入点
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper; // 注入 MyBatis 动态代理对象
    
    public User getUser(Long id) {
        return userMapper.findById(id); // 调用代理方法
    }
}

例如,在 Spring 和 MyBatis 整合的项目中,Dao 层接口(如 AccountDao)没有实现类却能自动注入,这背后是 MyBatis 的动态代理机制 和 Spring 的智能依赖注入 共同作用的结果。

核心机制解析

  1. MyBatis 的动态代理(核心)
    运行时生成实现类:MyBatis 在应用启动时,为每个 Mapper 接口动态生成代理实现类
    代理类处理 SQL:代理类负责解析接口方法上的注解(@Select, @Insert 等)并执行对应 SQL
    无需手动实现:开发者只需定义接口,MyBatis 自动处理实现
// 伪代码展示 MyBatis 动态代理工作原理
public class MapperProxy implements InvocationHandler {
    private SqlSession sqlSession;
    
    public Object invoke(Object proxy, Method method, Object[] args) {
        // 1. 解析方法上的注解(如 @Select)
        // 2. 获取 SQL 语句和参数映射
        // 3. 通过 SqlSession 执行数据库操作
        // 4. 返回结果
    }
}
  1. Spring 的 Bean 注册
    MapperScannerConfigurer 的作用:
@Bean
public MapperScannerConfigurer mapperScannerConfigurer() {
    MapperScannerConfigurer configurer = new MapperScannerConfigurer();
    configurer.setBasePackage("com.fjd.dao"); // 扫描 Dao 接口
    return configurer;
}

扫描注册过程:
扫描指定包下的接口
为每个接口生成代理对象
将代理对象注册为 Spring Bean
Bean 名称默认为接口名首字母小写(如 accountDao)

  1. Spring 的依赖注入
@Service
public class AccountServiceImpl {
    // 实际注入的是 MyBatis 生成的代理对象
    @Autowired
    private AccountDao accountDao; 
}

Spring 在容器中找到名为 accountDao 的 Bean(即代理对象)
完成接口类型的依赖注入

总结:Dao 层没有实现类却能自动注入,是因为:
1.动态代理机制:MyBatis 在运行时为 Mapper 接口生成代理实现
2.自动 Bean 注册:MapperScannerConfigurer 将代理对象注册为 Spring Bean
3.Spring 依赖注入:容器按类型/名称匹配接口和其代理实现

这种设计模式结合了两大框架的优势:
        1.MyBatis 提供灵活的 SQL 映射能力
        2.Spring 提供强大的依赖注入容器
        3.开发者享受简洁的接口编程体验

6. 集合注入所有实现

  • 条件:注入接口的所有实现
  • 结果:收集所有实现到集合中
public interface Validator {
    boolean validate(Object obj);
}

@Service
public class EmailValidator implements Validator { /*...*/ }

@Service
public class AgeValidator implements Validator { /*...*/ }

// 注入点
@Service
public class ValidationService {
    @Autowired
    private List<Validator> validators; // 注入 [EmailValidator, AgeValidator]
    
    public void validateAll(Object obj) {
        validators.forEach(v -> v.validate(obj));
    }
}

7. 使用 Map 注入命名实现

  • 条件:注入接口的所有实现,并按 Bean 名称组织
  • 结果:Map<Bean名称, 实现实例>
@Service
public class ValidationService {
    @Autowired
    private Map<String, Validator> validatorMap; 
    // 输出:{emailValidator: EmailValidator@123, ageValidator: AgeValidator@456}
}

8. 无实现类(注入失败)

  • 条件:接口没有任何实现类
  • 结果:启动时抛出 NoSuchBeanDefinitionException
public interface UnimplementedService {
    void doSomething();
}

// 注入点
@Service
public class SomeService {
    @Autowired
    private UnimplementedService service; // 启动失败:找不到 Bean
}

9. 抽象类注入

  • 条件:注入抽象类(特殊类型接口)
  • 结果:注入具体子类
public abstract class AbstractService {
    public abstract void execute();
}

@Service
public class ConcreteService extends AbstractService {
    @Override
    public void execute() { /*...*/ }
}

// 注入点
@Controller
public class ClientController {
    @Autowired
    private AbstractService service; // 注入 ConcreteService
}

10. JDK 动态代理接口注入(Spring AOP)

  • 条件:AOP 代理实现的接口
  • 结果:注入代理对象
public interface Loggable {
    void log();
}

@Service
public class DataService implements Loggable {
    @Override
    @LogExecution // AOP 切面注解
    public void log() { /*...*/ }
}

// 注入点
@Component
public class Client {
    @Autowired
    private Loggable loggable; // 注入 AOP 代理对象
    
    public void action() {
        loggable.log(); // 触发切面逻辑
    }
}

自动注入优先级总结:

当存在多个实现时,Spring 按以下顺序解析依赖:
@Qualifier 指定名称(最高优先级)
字段名匹配 Bean 名称
@Primary 标记的实现
类型匹配(当唯一时)
按 Bean 名称的自然排序(首字母顺序)

Spring 的注入规则总结

注入类型能否工作条件
@Autowired Interface有且仅有一个实现类
@Autowired Interface有多个实现但用 @Primary 指定默认
@Autowired Interface有多个实现但用 @Qualifier 指定
@Autowired Interface有多个实现且未指定
@Autowired ConcreteClass类本身是 Spring 组件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值