一、依赖注入回顾
1. 什么是依赖注入(DI)?
依赖注入:一种设计模式,将对象的创建和依赖管理交给容器,而不是在对象内部创建依赖。
2. DI 的三种方式
| 方式 | 说明 | 示例 |
|---|---|---|
| 构造器注入 | 通过构造函数注入 | public UserService(UserRepository repo) {...} |
| Setter 注入 | 通过 Setter 方法注入 | public void setUserRepository(UserRepository repo) {...} |
| 字段注入 | 直接通过 @Autowired 注入字段 | @Autowired private UserRepository repo; |
3. 推荐使用:构造器注入
@Service
public class UserService {
private final UserRepository userRepository;
// ✅ 推荐:构造器注入
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
为什么推荐构造器注入?
| 优点 | 说明 |
|---|---|
| 不可变性 | 使用 final 确保依赖不可变 |
| 便于测试 | 可以轻松使用 Mock 对象 |
| 必填依赖 | 确保对象创建时依赖已注入 |
| IDE 支持 | IDE 可以检测到缺失的依赖 |
二、Bean 的作用域
1. 常用作用域
| 作用域 | 说明 | 生命周期 |
|---|---|---|
| Singleton(默认) | 整个应用只创建一个实例 | 应用启动时创建,应用关闭时销毁 |
| Prototype | 每次请求都创建新实例 | 每次使用时创建 |
| Request | 每个 HTTP 请求创建一个实例 | 请求开始时创建,请求结束时销毁 |
| Session | 每个 HTTP Session 创建一个实例 | Session 创建时创建,Session 过期时销毁 |
2. Singleton(单例)
默认作用域:
@Component // 默认是 Singleton
public class UserService {
// ...
}
显式指定:
@Component
@Scope("singleton")
public class UserService {
// ...
}
特点:
- 整个应用只创建一个实例
- 线程安全需要自己保证
- 适用于无状态服务
3. Prototype(原型)
示例:
@Component
@Scope("prototype")
public class UserService {
// ...
}
特点:
- 每次使用都创建新实例
- 线程安全
- 适用于有状态对象
测试:
@SpringBootTest
public class ScopeTest {
@Autowired
private ApplicationContext context;
@Test
public void testPrototypeScope() {
UserService user1 = context.getBean(UserService.class);
UserService user2 = context.getBean(UserService.class);
// Prototype:每次都是新实例
assertNotSame(user1, user2);
}
}
4. Request 和 Session
注意: 需要启用 Web 环境。
@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestScopedBean {
// 每个 HTTP 请求创建一个实例
}
@Component
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class SessionScopedBean {
// 每个 HTTP Session 创建一个实例
}
三、Bean 的生命周期
1. Bean 生命周期流程
1. 实例化(Instance)
↓
2. 属性赋值(Populate Properties)
↓
3. BeanNameAware
↓
4. BeanFactoryAware
↓
5. ApplicationContextAware
↓
6. BeanPostProcessor(前置处理)
↓
7. InitializingBean / @PostConstruct
↓
8. 自定义 init-method
↓
9. Bean 准备就绪(Ready for Use)
↓
10. @PreDestroy
↓
11. DisposableBean
↓
12. 自定义 destroy-method
↓
13. Bean 销毁(Destroyed)
2. 生命周期回调
@PostConstruct(初始化)
@Component
public class UserService {
private UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@PostConstruct
public void init() {
System.out.println("UserService 初始化完成");
// 初始化逻辑
}
}
@PreDestroy(销毁)
@Component
public class UserService {
@PreDestroy
public void destroy() {
System.out.println("UserService 即将销毁");
// 清理逻辑
}
}
3. 完整生命周期示例
@Component
public class LifecycleBean implements BeanNameAware, ApplicationContextAware,
InitializingBean, DisposableBean {
private String beanName;
private ApplicationContext applicationContext;
// 1. 构造函数
public LifecycleBean() {
System.out.println("1. 构造函数执行");
}
// 2. BeanNameAware
@Override
public void setBeanName(String name) {
this.beanName = name;
System.out.println("2. BeanNameAware: " + name);
}
// 3. ApplicationContextAware
@Override
public void setApplicationContext(ApplicationContext context) {
this.applicationContext = context;
System.out.println("3. ApplicationContextAware");
}
// 4. @PostConstruct
@PostConstruct
public void postConstruct() {
System.out.println("4. @PostConstruct");
}
// 5. InitializingBean
@Override
public void afterPropertiesSet() {
System.out.println("5. InitializingBean.afterPropertiesSet");
}
// 6. 使用 Bean
public void doSomething() {
System.out.println("6. Bean 正在使用中");
}
// 7. @PreDestroy
@PreDestroy
public void preDestroy() {
System.out.println("7. @PreDestroy");
}
// 8. DisposableBean
@Override
public void destroy() {
System.out.println("8. DisposableBean.destroy");
}
}
四、自动装配(@Autowired)
1. @Autowired 的使用位置
| 位置 | 说明 | 示例 |
|---|---|---|
| 构造器 | 推荐方式 | @Autowired public Service(Repo repo) |
| Setter | 可选依赖 | @Autowired public void setRepo(Repo repo) |
| 字段 | 不推荐 | @Autowired private Repo repo; |
2. @RequiredArgsConstructor(推荐)
使用 Lombok 简化构造器注入:
@Service
@RequiredArgsConstructor // 自动生成构造器
public class UserService {
private final UserRepository userRepository; // final 字段自动注入
private EmailService emailService; // 非 final 字段不注入
}
等价于:
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
五、多个 Bean 的处理
1. 问题:多个 Bean 匹配
场景:
// 两个实现类
@Repository
public class MysqlUserRepository implements UserRepository {
// ...
}
@Repository
public class PostgresUserRepository implements UserRepository {
// ...
}
// 注入时出错
@Service
public class UserService {
@Autowired
private UserRepository userRepository; // ❌ 不知道注入哪个
}
错误:
No qualifying bean of type 'UserRepository' available:
expected single matching bean but found 2
2. 解决方案 1:@Primary
@Repository
@Primary // 优先注入这个 Bean
public class MysqlUserRepository implements UserRepository {
// ...
}
3. 解决方案 2:@Qualifier
@Service
public class UserService {
@Autowired
@Qualifier("mysqlUserRepository") // 指定 Bean 名称
private UserRepository userRepository;
}
4. 解决方案 3:参数名匹配
@Service
public class UserService {
private final UserRepository mysqlUserRepository; // 参数名与 Bean 名匹配
public UserService(UserRepository mysqlUserRepository) {
this.mysqlUserRepository = mysqlUserRepository;
}
}
六、条件注解
1. 常用条件注解
| 注解 | 作用 | 示例 |
|---|---|---|
@ConditionalOnClass | classpath 中存在指定类时生效 | @ConditionalOnClass(DataSource.class) |
@ConditionalOnMissingClass | classpath 中不存在指定类时生效 | @ConditionalOnMissingClass(RedisTemplate.class) |
@ConditionalOnBean | 容器中存在指定 Bean 时生效 | @ConditionalOnBean(DataSource.class) |
@ConditionalOnMissingBean | 容器中不存在指定 Bean 时生效 | @ConditionalOnMissingBean(DataSource.class) |
@ConditionalOnProperty | 配置文件中满足指定属性时生效 | @ConditionalOnProperty(name="app.cache.enabled") |
2. 条件注解示例
@ConditionalOnProperty
@Configuration
@ConditionalOnProperty(name = "app.cache.enabled", havingValue = "true")
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
// 只有 app.cache.enabled=true 时才创建 Bean
}
}
application.properties:
app.cache.enabled=true
@ConditionalOnMissingBean
@Configuration
public class DataSourceConfig {
@Bean
@ConditionalOnMissingBean(DataSource.class) // 用户没有自定义时才创建
public DataSource dataSource() {
// 创建默认 DataSource
}
}
用户自定义:
@Configuration
public class CustomDataSourceConfig {
@Bean
public DataSource customDataSource() {
// 用户自定义 DataSource(优先级更高)
return new CustomDataSource();
}
}
七、Bean 的创建时机
1. 懒加载(Lazy Loading)
默认行为:
- Singleton Bean 在应用启动时创建
懒加载:
- Bean 在第一次使用时创建
2. @Lazy 注解
@Component
@Lazy // 懒加载
public class HeavyService {
public HeavyService() {
System.out.println("HeavyService 创建(可能耗时较长)");
}
}
注入懒加载 Bean:
@Service
public class UserService {
@Autowired
@Lazy // 懒加载
private HeavyService heavyService;
}
3. 测试懒加载
@SpringBootTest
public class LazyLoadTest {
@Autowired
private ApplicationContext context;
@Test
public void testLazyLoad() {
System.out.println("应用启动");
// 第一次使用时才创建
HeavyService heavyService = context.getBean(HeavyService.class);
heavyService.doSomething();
// 第二次使用:使用缓存的实例
HeavyService heavyService2 = context.getBean(HeavyService.class);
assertSame(heavyService, heavyService2);
}
}
八、循环依赖
1. 什么是循环依赖?
定义: 两个或多个 Bean 互相依赖,导致无法创建。
示例:
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB;
}
@Service
public class ServiceB {
@Autowired
private ServiceA serviceA; // 循环依赖
}
2. Spring 如何处理循环依赖?
Singleton 作用域:
- Spring 使用三级缓存解决 Singleton 循环依赖
- 构造器注入无法解决循环依赖
Prototype 作用域:
- 不解决循环依赖,会抛出异常
3. 解决循环依赖的方法
方法 1:使用 Setter 注入(推荐)
@Service
public class ServiceA {
private ServiceB serviceB;
@Autowired
public void setServiceB(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
@Service
public class ServiceB {
private ServiceA serviceA;
@Autowired
public void setServiceA(ServiceA serviceA) {
this.serviceA = serviceA;
}
}
方法 2:使用 @Lazy
@Service
public class ServiceA {
@Autowired
@Lazy // 懒加载,打破循环依赖
private ServiceB serviceB;
}
@Service
public class ServiceB {
@Autowired
private ServiceA serviceA;
}
方法 3:重构代码(最佳)
问题根源: 设计不合理,应该拆分职责。
重构:
// 公共接口
public interface CommonService {
void doCommonThing();
}
@Service
public class ServiceA implements CommonService {
@Autowired
private CommonService commonService;
@Override
public void doCommonThing() {
// ...
}
}
@Service
public class ServiceB implements CommonService {
@Autowired
private CommonService commonService;
@Override
public void doCommonThing() {
// ...
}
}
九、最佳实践
1. 依赖注入方式选择
| 场景 | 推荐方式 |
|---|---|
| 必填依赖 | 构造器注入 + final |
| 可选依赖 | Setter 注入 |
| 字段注入 | 不推荐使用 |
2. Bean 作用域选择
| 场景 | 推荐作用域 |
|---|---|
| 无状态服务 | Singleton(默认) |
| 有状态对象 | Prototype |
| 请求级别数据 | Request |
| Session 级别数据 | Session |
3. 命名规范
| 类型 | 规范 | 示例 |
|---|---|---|
| Bean 名称 | 类名首字母小写 | userService |
| 配置类 | *Config | DataSourceConfig |
十、总结
| 概念 | 说明 |
|---|---|
| 依赖注入 | 将对象的创建交给容器 |
| 构造器注入 | 推荐方式,使用 final |
| Bean 作用域 | Singleton、Prototype、Request、Session |
| 生命周期 | @PostConstruct、@PreDestroy |
| 自动装配 | @Autowired、@Primary、@Qualifier |
| 条件注解 | 根据条件创建 Bean |
| 懒加载 | 首次使用时创建 |
| 循环依赖 | 使用 Setter 注入或 @Lazy |