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 的智能依赖注入 共同作用的结果。
核心机制解析
- 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. 返回结果
}
}
- Spring 的 Bean 注册
MapperScannerConfigurer 的作用:
@Bean
public MapperScannerConfigurer mapperScannerConfigurer() {
MapperScannerConfigurer configurer = new MapperScannerConfigurer();
configurer.setBasePackage("com.fjd.dao"); // 扫描 Dao 接口
return configurer;
}
扫描注册过程:
扫描指定包下的接口
为每个接口生成代理对象
将代理对象注册为 Spring Bean
Bean 名称默认为接口名首字母小写(如 accountDao)
- 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 组件 |