IOC容器相关问题

总结摘要
总结Spring中IOC容器相关知识点可能提的问题

针对IOC容器相关问题,我准备了一个“一句话原理 + 一句话源码 + 一句话项目/场景”的结构化回答,体现深度同时展现实战能力。

基础概念

什么是 IOC(控制反转)?它解决了什么问题?

1. IOC核心原理

一句话原理:IOC(Inversion of Control)是一种设计原则,将对象的创建权依赖管理权从应用程序代码反转给容器(如Spring),由容器负责对象的生命周期和依赖装配,实现控制权的转移

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// 传统方式:主动创建依赖(控制权在应用程序)
public class UserService {
    // 主动创建依赖对象
    private UserDao userDao = new UserDaoImpl();  // 硬编码耦合
    private EmailService emailService = new EmailServiceImpl();
    
    public void register(User user) {
        userDao.save(user);
        emailService.sendWelcomeEmail(user);
    }
}

// IOC方式:被动等待注入(控制权反转给容器)
public class UserService {
    // 声明依赖,但不创建
    private UserDao userDao;      // 等待容器注入
    private EmailService emailService;
    
    // 容器通过构造器注入
    public UserService(UserDao userDao, EmailService emailService) {
        this.userDao = userDao;
        this.emailService = emailService;
    }
    
    public void register(User user) {
        userDao.save(user);        // 直接使用注入的对象
        emailService.sendWelcomeEmail(user);
    }
}

// Spring IOC容器核心实现(简化版)
public class SimpleIOCContainer {
    private Map<String, Object> beans = new ConcurrentHashMap<>();
    
    // 扫描并创建Bean
    public void refresh() {
        // 1. 扫描指定包下的所有类
        Set<Class<?>> classes = scanPackages("com.example");
        
        // 2. 筛选有@Component注解的类
        for (Class<?> clazz : classes) {
            if (clazz.isAnnotationPresent(Component.class)) {
                // 3. 通过反射创建实例
                Object instance = clazz.newInstance();
                String beanName = getBeanName(clazz);
                beans.put(beanName, instance);
            }
        }
        
        // 4. 依赖注入(处理@Autowired)
        for (Object bean : beans.values()) {
            for (Field field : bean.getClass().getDeclaredFields()) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    Object dependency = beans.get(field.getName());
                    field.setAccessible(true);
                    field.set(bean, dependency);  // 注入依赖
                }
            }
        }
    }
    
    // 获取Bean
    public Object getBean(String name) {
        return beans.get(name);
    }
}

项目场景:在微服务架构中,一个订单服务需要依赖用户服务、商品服务、库存服务等多个组件。通过Spring IOC容器管理这些依赖,服务类只需声明接口,容器自动注入具体实现,代码从"主动创建"变成"被动接收",大大降低了耦合度。

2. IOC解决的核心问题

一句话原理:IOC解决了传统开发中的四大痛点硬编码耦合(依赖写死在代码里)、难以测试(无法替换依赖实现)、扩展困难(修改实现需改代码)、生命周期管理复杂(对象创建销毁混乱)。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
// 问题1:硬编码耦合
public class TraditionalService {
    // 直接new具体实现,耦合严重
    private MysqlDao dao = new MysqlDao();  // 如果换成RedisDao,必须改代码
    
    public void process() {
        dao.save(data);
    }
}

// IOC解耦后
public class BetterService {
    private Dao dao;  // 依赖接口,不依赖实现
    
    public BetterService(Dao dao) {  // 容器注入任意Dao实现
        this.dao = dao;
    }
}

// 问题2:难以测试
public class HardToTestService {
    private EmailSender sender = new SmtpEmailSender();  // 真实发送邮件
    
    public void notify(User user) {
        sender.send(user.getEmail(), "message");  // 单元测试会真发邮件
    }
}

// IOC轻松测试
public class EasyToTestService {
    private EmailSender sender;  // 等待注入
    
    public void notify(User user) {
        sender.send(user.getEmail(), "message");
    }
}

// 测试时可以注入Mock对象
public class ServiceTest {
    @Test
    public void testNotify() {
        // 注入Mock对象,不真正发送邮件
        EmailSender mockSender = mock(EmailSender.class);
        EasyToTestService service = new EasyToTestService(mockSender);
        
        service.notify(user);
        
        // 验证Mock方法被调用
        verify(mockSender).send(anyString(), anyString());
    }
}

// 问题3:扩展困难
public class OrderService {
    // 想要增加日志、缓存等功能,需要修改代码
    private PaymentService paymentService = new AlipayService();
}

// IOC方便扩展
@Component
public class OrderService {
    @Autowired
    private PaymentService paymentService;  // 可以注入任何PaymentService实现
}

// 新增微信支付,只需添加新实现,无需修改OrderService
@Component("wechatPay")
public class WechatPayService implements PaymentService {
    // 微信支付实现
}

// 问题4:生命周期管理复杂
public class ComplexLifecycle {
    // 手动管理资源
    private Connection conn;
    private Cache cache;
    
    public void init() {  // 手动初始化
        conn = DriverManager.getConnection(url);
        cache = new Cache();
    }
    
    public void destroy() {  // 手动销毁
        conn.close();
        cache.clear();
    }
}

// IOC容器自动管理
@Component
public class SimpleLifecycle {
    @Autowired
    private Connection conn;  // 容器注入并管理
    
    @Autowired
    private Cache cache;       // 容器管理生命周期
    
    // 无需init/destroy,容器负责
}

项目场景:在支付系统重构时,需要将原有的支付宝支付扩展到微信支付、银联支付。如果使用传统方式,需要在OrderService中修改大量代码。而基于IOC的设计,只需新增WechatPayService实现PaymentService接口,通过配置注入即可,原有代码零修改,体现了IOC对扩展开放、对修改封闭的设计原则。

3. IOC的两种实现方式

一句话原理:IOC主要通过**依赖注入(DI)**实现,包含三种注入方式:构造器注入(强制依赖,不可变)、Setter注入(可选依赖)、字段注入,Spring推荐构造器注入保证依赖完整性。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/**
 * IOC的三种实现方式
 */
public class DependencyInjectionTypes {
    
    // 1. 构造器注入(Constructor Injection)- 推荐方式
    @Component
    public class ConstructorInjection {
        private final UserDao userDao;      // final保证不可变
        private final EmailService emailService;
        
        // Spring自动调用构造器注入
        public ConstructorInjection(UserDao userDao, EmailService emailService) {
            this.userDao = userDao;          // 构造时就必须传入
            this.emailService = emailService;
        }
    }
    
    // 2. Setter注入(Setter Injection)- 可选依赖
    @Component
    public class SetterInjection {
        private LogService logService;       // 可选依赖,允许null
        
        @Autowired
        public void setLogService(LogService logService) {
            this.logService = logService;    // 通过setter注入
        }
    }
    
    // 3. 字段注入(Field Injection)- 最简洁但测试不便
    @Component
    public class FieldInjection {
        @Autowired
        private OrderDao orderDao;           // 直接注解字段
        
        @Autowired
        private PaymentService paymentService;
    }
    
    // Spring IOC容器处理注入的源码(简化版)
    public class AutowiredProcessor {
        public void processInjection(Object bean) {
            // 处理字段注入
            for (Field field : bean.getClass().getDeclaredFields()) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    Object dependency = getBean(field.getType());
                    field.setAccessible(true);
                    field.set(bean, dependency);
                }
            }
            
            // 处理Setter注入
            for (Method method : bean.getClass().getMethods()) {
                if (method.isAnnotationPresent(Autowired.class)) {
                    Class<?>[] paramTypes = method.getParameterTypes();
                    Object[] dependencies = new Object[paramTypes.length];
                    for (int i = 0; i < paramTypes.length; i++) {
                        dependencies[i] = getBean(paramTypes[i]);
                    }
                    method.invoke(bean, dependencies);
                }
            }
            
            // 构造器注入在Bean创建时处理
        }
    }
}

项目场景:在开发订单服务时,强制依赖(如OrderRepository、PaymentClient)使用构造器注入,确保对象创建后立即可用;可选依赖(如LogService、MetricService)使用Setter注入,方便测试时替换。这种组合既保证了依赖完整性,又提供了灵活性。

完整实战指南

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
/**
 * IOC完整实战指南
 */
public class IOCCompleteGuide {
    
    /**
     * 1. 传统方式 vs IOC方式对比
     */
    public class Comparison {
        
        // 传统方式(主动创建)
        public class TraditionalOrderService {
            private OrderDao orderDao = new OrderDaoImpl();      // 耦合具体类
            private InventoryClient inventoryClient = new InventoryClientImpl(); // 硬编码
            private MessageSender messageSender = new SmsSender(); // 难以替换
            
            public void createOrder(Order order) {
                orderDao.save(order);
                inventoryClient.deductStock(order.getProductId());
                messageSender.send("订单创建成功");
            }
        }
        
        // IOC方式(被动接收)
        @Service
        public class IOCOrderService {
            private final OrderDao orderDao;
            private final InventoryClient inventoryClient;
            private final MessageSender messageSender;
            
            // 构造器注入,依赖清晰
            public IOCOrderService(OrderDao orderDao, 
                                   InventoryClient inventoryClient,
                                   MessageSender messageSender) {
                this.orderDao = orderDao;
                this.inventoryClient = inventoryClient;
                this.messageSender = messageSender;
            }
            
            public void createOrder(Order order) {
                orderDao.save(order);
                inventoryClient.deductStock(order.getProductId());
                messageSender.send("订单创建成功");
            }
        }
        
        // 优势对比:
        // 传统:紧耦合、难测试、难扩展
        // IOC:松耦合、易测试、易扩展
    }
    
    /**
     * 2. IOC带来的好处实战
     */
    public class IOCBenefits {
        
        // 好处1:单元测试容易
        @Test
        public void testOrderService() {
            // 轻松注入Mock对象
            OrderDao mockDao = mock(OrderDao.class);
            InventoryClient mockClient = mock(InventoryClient.class);
            MessageSender mockSender = mock(MessageSender.class);
            
            IOCOrderService service = new IOCOrderService(mockDao, mockClient, mockSender);
            service.createOrder(order);
            
            // 验证交互
            verify(mockDao).save(order);
            verify(mockClient).deductStock(any());
        }
        
        // 好处2:轻松替换实现
        @Configuration
        public class AppConfig {
            @Bean
            @ConditionalOnProperty(name = "cache.type", havingValue = "redis")
            public Cache redisCache() {
                return new RedisCache();  // 条件注入Redis实现
            }
            
            @Bean
            @ConditionalOnProperty(name = "cache.type", havingValue = "local")
            public Cache localCache() {
                return new LocalCache();  // 条件注入本地实现
            }
        }
        
        // 好处3:生命周期管理
        @Component
        public class LifecycleBean implements InitializingBean, DisposableBean {
            @Override
            public void afterPropertiesSet() {
                // 所有依赖注入完成后执行
                System.out.println("Bean初始化完成");
            }
            
            @Override
            public void destroy() {
                // 容器关闭前执行
                System.out.println("Bean销毁");
            }
        }
    }
    
    /**
     * 3. Spring IOC核心注解
     */
    public class IOCAnnotations {
        
        // @Component - 标记为Bean
        @Component
        public class UserService { }
        
        // @Service - 业务层
        @Service
        public class OrderService { }
        
        // @Repository - 数据访问层
        @Repository
        public class UserDao { }
        
        // @Controller - 控制层
        @Controller
        public class UserController { }
        
        // @Autowired - 依赖注入
        @Service
        public class PaymentService {
            @Autowired
            private OrderDao orderDao;  // 字段注入
            
            private UserDao userDao;
            
            @Autowired
            public void setUserDao(UserDao userDao) {  // Setter注入
                this.userDao = userDao;
            }
        }
        
        // @Qualifier - 指定Bean名称
        @Service
        public class MessageService {
            @Autowired
            @Qualifier("smsSender")
            private MessageSender sender;  // 注入名为"smsSender"的Bean
        }
        
        // @Primary - 主要Bean
        @Component
        @Primary
        public class MainDatabase implements Database { }
    }
    
    /**
     * 4. 手写简单IOC容器
     */
    public class SimpleIOC {
        
        public static void main(String[] args) throws Exception {
            // 创建容器
            MyIOCContainer container = new MyIOCContainer("com.example");
            
            // 获取Bean
            UserService userService = (UserService) container.getBean("userService");
            userService.register(new User());
        }
    }
    
    // 简易IOC容器实现
    static class MyIOCContainer {
        private Map<String, Object> beans = new ConcurrentHashMap<>();
        
        public MyIOCContainer(String basePackage) throws Exception {
            // 1. 扫描包下的所有类
            Set<Class<?>> classes = scanPackage(basePackage);
            
            // 2. 创建所有Bean实例
            for (Class<?> clazz : classes) {
                if (clazz.isAnnotationPresent(Component.class)) {
                    String beanName = getBeanName(clazz);
                    Object instance = clazz.getDeclaredConstructor().newInstance();
                    beans.put(beanName, instance);
                }
            }
            
            // 3. 处理依赖注入
            for (Object bean : beans.values()) {
                for (Field field : bean.getClass().getDeclaredFields()) {
                    if (field.isAnnotationPresent(Autowired.class)) {
                        Object dependency = beans.get(field.getName());
                        field.setAccessible(true);
                        field.set(bean, dependency);
                    }
                }
            }
        }
        
        public Object getBean(String name) {
            return beans.get(name);
        }
        
        private Set<Class<?>> scanPackage(String basePackage) {
            // 实际项目中会用反射扫描包
            return new HashSet<>();
        }
        
        private String getBeanName(Class<?> clazz) {
            // 默认类名首字母小写
            String simpleName = clazz.getSimpleName();
            return Character.toLowerCase(simpleName.charAt(0)) + simpleName.substring(1);
        }
    }
}

/**
 * IOC总结表
 * 
 * 概念        说明
 * 控制反转    对象创建权从代码反转到容器
 * 依赖注入    容器负责装配依赖关系
 * Bean       由IOC容器管理的对象
 * 容器        Bean的创建者和管理者
 * 
 * 注入方式    优点                缺点               适用场景
 * 构造器注入  依赖不可变,保证完整性   对象创建参数多      强制依赖
 * Setter注入  可选依赖,方便修改       依赖可能null       可选依赖
 * 字段注入    代码简洁                难测试            快速开发
 * 
 * 解决的问题:
 * 1. 解耦:依赖接口不依赖实现
 * 2. 可测试:轻松注入Mock对象
 * 3. 可扩展:新增实现无需修改代码
 * 4. 生命周期管理:容器统一管理
 */

// 面试金句
// "IOC就像'权力下放':以前你是'皇帝',要亲自管理所有大臣(创建对象)、
//  安排他们的工作(处理依赖)、操心他们的生老病死(生命周期);
//  有了IOC容器,你成了'君主立宪',只需告诉容器'我需要什么样的大臣',
//  容器就给你配好,用完还会自动清理。
//  在微服务架构中,一个订单服务可能依赖10+个组件,如果都手动创建,
//  代码会臃肿不堪。通过Spring IOC,依赖关系变得清晰,单元测试变得简单,
//  扩展新支付方式零成本。这就是IOC的魅力:让我们专注于业务逻辑,
//  把对象的'生老病死'交给容器打理"

DI(依赖注入)有哪些方式?@Autowired 和 @Resource 的区别?

1. 依赖注入的三种方式

一句话原理:依赖注入(DI)有三种实现方式:构造器注入(强制依赖,不可变)、Setter注入(可选依赖,可重新绑定)、字段注入(最简洁,但测试不便),Spring推荐构造器注入保证依赖完整性和不可变性。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/**
 * 依赖注入三种方式源码实现
 */
public class DependencyInjectionTypes {
    
    // 1. 构造器注入(Constructor Injection)- Spring官方推荐
    @Service
    public class ConstructorInjectionService {
        private final UserDao userDao;          // final保证不可变
        private final EmailService emailService; // 强制依赖必须提供
        
        // Spring自动调用构造器注入
        public ConstructorInjectionService(UserDao userDao, EmailService emailService) {
            this.userDao = userDao;              // 对象创建时必须传入
            this.emailService = emailService;
        }
        
        // 优点:依赖不可变,对象创建后立即可用,便于测试
        // 缺点:依赖过多时构造器参数冗长
    }
    
    // 2. Setter注入(Setter Injection)
    @Service
    public class SetterInjectionService {
        private LogService logService;           // 可选依赖,允许null
        private MetricService metricService;     // 可在运行时更换
        
        @Autowired
        public void setLogService(LogService logService) {
            this.logService = logService;        // 通过setter注入
        }
        
        @Autowired
        public void setMetricService(MetricService metricService) {
            this.metricService = metricService;
        }
        
        // 优点:可选依赖,可在运行时重新注入
        // 缺点:依赖可能为null,需要额外检查
    }
    
    // 3. 字段注入(Field Injection)- 最常用但争议最大
    @Service
    public class FieldInjectionService {
        @Autowired
        private OrderDao orderDao;                // 直接注解字段
        
        @Autowired
        private PaymentService paymentService;    // 简洁但隐藏依赖
        
        public void processOrder(Order order) {
            orderDao.save(order);                 // 直接使用
            paymentService.pay(order);
        }
        
        // 优点:代码最简洁
        // 缺点:无法声明final,测试难以注入,隐藏依赖关系
    }
    
    // Spring处理字段注入的源码(简化版)
    public class AutowiredAnnotationBeanPostProcessor {
        
        // 处理@Autowired注解的核心方法
        public void processInjection(Object bean) {
            Class<?> clazz = bean.getClass();
            
            // 处理字段注入
            for (Field field : clazz.getDeclaredFields()) {
                Autowired autowired = field.getAnnotation(Autowired.class);
                if (autowired != null) {
                    Object dependency = getDependency(field.getType());
                    field.setAccessible(true);
                    field.set(bean, dependency);  // 直接赋值
                }
            }
            
            // 处理方法注入(包括Setter)
            for (Method method : clazz.getMethods()) {
            Autowired autowired = method.getAnnotation(Autowired.class);
                if (autowired != null) {
                    Class<?>[] paramTypes = method.getParameterTypes();
                    Object[] args = new Object[paramTypes.length];
                    for (int i = 0; i < paramTypes.length; i++) {
                        args[i] = getDependency(paramTypes[i]);
                    }
                    method.invoke(bean, args);     // 调用方法注入
                }
            }
        }
    }
}

项目场景:在开发订单核心服务时,强制依赖(如OrderRepository、PaymentClient)使用构造器注入,确保对象创建后立即可用;可选依赖(如LogService、MetricService)使用Setter注入,方便在测试时替换为Mock对象;快速原型开发时偶尔用字段注入,但生产代码坚持构造器注入,保证代码质量和可测试性。

2. @Autowired 和 @Resource 核心区别

一句话原理@Autowired是Spring注解,按类型(byType)注入,可配合@Qualifier按名称;@Resource是JSR-250标准注解,先按名称(byName)再按类型,来源于Java原生规范,两者在装配策略上存在本质差异。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/**
 * @Autowired vs @Resource 深度对比
 */
public class AutowiredVsResource {
    
    // 1. @Autowired - 默认按类型装配
    @Service
    public class AutowiredExample {
        
        @Autowired
        private UserDao userDao;  // 按类型查找UserDao
        
        @Autowired
        @Qualifier("mysqlDao")   // 结合Qualifier按名称
        private UserDao mysqlDao;
        
        @Autowired(required = false)  // 非必需依赖
        private CacheService cacheService;
        
        // 构造器注入也支持@Autowired
        @Autowired
        public AutowiredExample(UserDao userDao) {
            this.userDao = userDao;
        }
    }
    
    // 2. @Resource - 默认按名称装配
    @Service
    public class ResourceExample {
        
        @Resource(name = "mysqlDao")  // 指定名称
        private UserDao mysqlDao;
        
        @Resource  // 先按名称"userDao"找,找不到再按类型
        private UserDao userDao;
        
        @Resource(type = UserDao.class)  // 指定类型
        private UserDao dao;
        
        // @Resource不支持构造器注入
    }
    
    // 3. Spring源码中的处理差异
    public class AutowiredAnnotationProcessor {
        
        // 处理@Autowired
        public void processAutowired(Field field, Object bean) {
            // 1. 先按类型查找
            Object dependency = beanFactory.getBean(field.getType());
            
            // 2. 如果类型匹配到多个,检查是否有@Primary
            // 3. 如果还有多个,尝试按名称匹配
            // 4. 如果还有多个,抛出异常
            field.set(bean, dependency);
        }
        
        // 处理@Resource(通过CommonAnnotationBeanPostProcessor)
        public void processResource(Field field, Object bean) {
            String name = getResourceName(field);  // 获取@Resource的name
            
            if (name != null) {
                // 1. 按指定名称查找
                dependency = beanFactory.getBean(name);
            } else {
                // 2. 按字段名查找
                dependency = beanFactory.getBean(field.getName());
                if (dependency == null) {
                    // 3. 最后按类型查找
                    dependency = beanFactory.getBean(field.getType());
                }
            }
            field.set(bean, dependency);
        }
    }
}

项目场景:在一个多数据源项目中,同时配置了MySQLDao、OracleDao、RedisDao等多个同类型Bean。此时使用@Autowired会因类型模糊而报错,必须配合@Qualifier;而@Resource通过指定name=“mysqlDao"更直观地选择具体实现,避免了Qualifier的额外配置。

3. 详细对比表格与选择建议

一句话原理:选择@Autowired还是@Resource取决于框架依赖(是否绑定Spring)、装配需求(按类型还是名称)、代码规范(团队约定),Spring项目推荐@Autowired,跨框架项目推荐@Resource。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/**
 * 详细对比与选择指南
 */
public class ComparisonGuide {
    
    // 对比表格(代码注释形式)
    // | 特性               | @Autowired                          | @Resource                          |
    // |-------------------|-------------------------------------|-----------------------------------|
    // | 所属框架           | Spring                              | Java标准(JSR-250)                |
    // | 默认装配方式       | 按类型(byType)                    | 按名称(byName)                    |
    // | 指定名称           | @Qualifier                          | name属性                           |
    // | 指定类型           | 不支持(靠泛型)                     | type属性                           |
    // | 支持构造器注入     | 是                                   | 否                                |
    // | 支持Setter注入     | 是                                   | 是                                |
    // | 支持字段注入       | 是                                   | 是                                |
    // | required属性       | 支持(required=false)               | 不支持(必须存在)                  |
    // | 处理优先级         | 先类型,再Qualifier                  | 先名称,再类型                      |
    
    // 1. 场景一:单类型单Bean - 两者通用
    @Service
    public class SingleBeanCase {
        @Autowired
        private OrderDao orderDao;  // 只有一个OrderDao实现
        
        @Resource
        private PaymentService paymentService;  // 只有一个实现
    }
    
    // 2. 场景二:多实现需指定名称 - @Resource更简洁
    @Service
    public class MultipleImplCase {
        // @Autowired方式:需要Qualifier
        @Autowired
        @Qualifier("mysqlDao")
        private UserDao mysqlDao;
        
        // @Resource方式:直接指定name
        @Resource(name = "oracleDao")
        private UserDao oracleDao;
        
        @Resource(name = "redisDao")
        private UserDao redisDao;
    }
    
    // 3. 场景三:按类型匹配+主次之分 - @Autowired支持@Primary
    @Configuration
    public class PrimaryConfig {
        @Bean
        @Primary  // 主Bean
        public CacheService redisCache() {
            return new RedisCache();
        }
        
        @Bean
        public CacheService localCache() {
            return new LocalCache();  // 次选
        }
    }
    
    @Service
    public class CacheUserService {
        @Autowired
        private CacheService cache;  // 自动注入@Primary的RedisCache
    }
    
    // 4. 场景四:非Spring环境 - 只能用@Resource
    public class NonSpringCase {
        @Resource
        private DataSource dataSource;  // 在JavaEE容器中可用
    }
    
    // 5. 场景五:可选依赖 - @Autowired支持required=false
    @Service
    public class OptionalDependencyCase {
        @Autowired(required = false)
        private MetricService metricService;  // 有就注入,没有就null
        
        @Resource  // 如果Bean不存在,直接报错
        private LogService logService;
    }
    
    // 6. 选择建议
    public class SelectionAdvice {
        // 推荐@Autowired的场景:
        // - Spring Boot项目
        // - 需要@Primary功能
        // - 需要构造器注入
        // - 需要可选依赖(required=false)
        
        // 推荐@Resource的场景:
        // - 多实现需要明确指定名称
        // - 可能迁移到其他JavaEE容器
        // - 遗留系统迁移
        // - 代码规范要求使用Java标准
        
        // 团队约定:统一使用一种
        // 大多数Spring团队统一使用@Autowired
    }
}

项目场景:在一个支付系统中,有AlipayService、WechatPayService、UnionPayService等多个实现。团队约定:业务层统一使用@Resource(name=“xxx”)明确指定支付方式,配置层使用@Autowired按类型注入数据源等单一实现。这种混合策略既保证了代码清晰,又充分利用了两种注解的优势。

完整实战指南

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/**
 * 依赖注入完整实战指南
 */
public class DICompleteGuide {
    
    /**
     * 1. 构造器注入最佳实践
     */
    @Service
    public class BestPracticeService {
        // 使用final修饰,保证不可变
        private final OrderRepository orderRepository;
        private final PaymentClient paymentClient;
        private final MessageSender messageSender;
        
        // Lombok @RequiredArgsConstructor 可自动生成
        public BestPracticeService(OrderRepository orderRepository,
                                   PaymentClient paymentClient,
                                   MessageSender messageSender) {
            this.orderRepository = orderRepository;
            this.paymentClient = paymentClient;
            this.messageSender = messageSender;
        }
        
        // 业务方法直接使用final字段
        public void processOrder(Order order) {
            orderRepository.save(order);
            paymentClient.pay(order);
            messageSender.send("订单处理成功");
        }
    }
    
    /**
     * 2. Setter注入用于可选依赖
     */
    @Service
    public class OptionalDependenciesService {
        private final OrderRepository orderRepository;  // 强制依赖用构造器
        
        private LogService logService;        // 可选依赖用Setter
        private MetricService metricService;
        
        public OptionalDependenciesService(OrderRepository orderRepository) {
            this.orderRepository = orderRepository;
        }
        
        @Autowired(required = false)
        public void setLogService(LogService logService) {
            this.logService = logService;
        }
        
        @Autowired(required = false)
        public void setMetricService(MetricService metricService) {
            this.metricService = metricService;
        }
        
        public void process() {
            if (logService != null) {
                logService.log("processing");
            }
            // 核心业务
        }
    }
    
    /**
     * 3. 多实现注入实战
     */
    @Service
    public class MultipleImplementationsService {
        
        // 方案1:使用@Qualifier明确指定
        @Autowired
        @Qualifier("redisCache")
        private Cache redisCache;
        
        // 方案2:使用@Resource指定名称
        @Resource(name = "localCache")
        private Cache localCache;
        
        // 方案3:注入所有实现
        @Autowired
        private List<Cache> allCaches;  // 注入所有Cache实现
        
        // 方案4:注入Map(bean名称->实例)
        @Autowired
        private Map<String, Cache> cacheMap;
        
        public void useAllCaches() {
            allCaches.forEach(Cache::clear);  // 清除所有缓存
            cacheMap.get("redisCache").get("key");  // 按名称获取
        }
    }
    
    /**
     * 4. 循环依赖解决
     */
    @Service
    public class CircularDependencyA {
        private CircularDependencyB b;
        
        @Autowired
        public CircularDependencyA(CircularDependencyB b) {  // 构造器注入会导致循环依赖错误
            this.b = b;
        }
    }
    
    @Service
    public class CircularDependencyB {
        private CircularDependencyA a;
        
        @Autowired
        public void setA(CircularDependencyA a) {  // 改为Setter注入
            this.a = a;
        }
    }
    
    // Spring解决循环依赖的三级缓存机制
    public class SpringCircularReference {
        // 一级缓存:singletonObjects(已完成Bean)
        // 二级缓存:earlySingletonObjects(半成品Bean)
        // 三级缓存:singletonFactories(Bean工厂)
        
        // 构造器注入无法解决循环依赖
        // Setter注入可以解决(通过提前暴露半成品)
    }
    
    /**
     * 5. 单元测试中的注入
     */
    public class DIUnitTest {
        
        @Test
        public void testConstructorInjection() {
            // 构造器注入测试最简单
            UserDao mockDao = mock(UserDao.class);
            EmailService mockEmail = mock(EmailService.class);
            
            UserService service = new UserService(mockDao, mockEmail);
            service.register(user);
            
            verify(mockDao).save(user);
        }
        
        @Test
        public void testFieldInjection() throws Exception {
            // 字段注入需要反射,较麻烦
            UserService service = new UserService();
            
            Field daoField = service.getClass().getDeclaredField("userDao");
            daoField.setAccessible(true);
            daoField.set(service, mock(UserDao.class));  // 反射注入
            
            service.register(user);
        }
        
        @Test
        public void testSetterInjection() {
            UserService service = new UserService();
            service.setUserDao(mock(UserDao.class));  // Setter注入
            service.register(user);
        }
    }
}

/**
 * 依赖注入总结表
 * 
 * 注入方式    优点                缺点               适用场景
 * 构造器注入  不可变,依赖完整      参数过多            强制依赖
 * Setter注入  可选,可重注入        可能null          可选依赖
 * 字段注入    代码简洁             难测试,隐藏依赖    快速原型
 * 
 * 注解对比
 * 特性        @Autowired          @Resource
 * 来源        Spring               Java标准
 * 默认方式    按类型               按名称
 * 指定名称    @Qualifier           name属性
 * 指定类型    泛型                  type属性
 * 必需性      required属性         必须存在
 * 
 * 最佳实践:
 * 1. 优先构造器注入
 * 2. 明确多实现时用@Resource
 * 3. 单一实现用@Autowired
 * 4. 团队内统一规范
 */

// 面试金句
// "依赖注入就像'送快递'的三种方式:
//  构造器注入是'当面签收'(必须亲手交给收件人,最安全);
//  Setter注入是'放快递柜'(收件人随时可取,灵活但可能丢件);
//  字段注入是'扔门口'(最省事,但谁都能捡,容易丢)。
//  @Autowired和@Resource的区别就像'按人脸识别'和'按名字叫号':
//  @Autowired看你是'哪类人'(按类型),如果同类人多,还得喊个'小名'(@Qualifier);
//  @Resource先叫你'大名'(按名称),找不到再问你是'干啥的'(按类型)。
//  在支付系统中,我用@Resource明确指定支付宝/微信支付,
//  @Autowired自动装配唯一的数据源各取所长"

Spring 中有哪些 Bean 的作用域?不同作用域的使用场景是什么?

1. 五大标准作用域

一句话原理:Spring提供五种标准Bean作用域singleton(单例,默认)、prototype(原型)、request(请求级)、session(会话级)、application(应用级),分别对应不同粒度的对象生命周期管理。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// Spring中作用域定义的源码
public interface ConfigurableBeanFactory {
    // 单例作用域:整个容器只创建一个实例
    String SCOPE_SINGLETON = "singleton";
    
    // 原型作用域:每次获取都创建新实例
    String SCOPE_PROTOTYPE = "prototype";
}

public interface WebApplicationContext {
    // 请求作用域:每个HTTP请求创建一个实例
    String SCOPE_REQUEST = "request";
    
    // 会话作用域:每个HTTP会话创建一个实例
    String SCOPE_SESSION = "session";
    
    // 应用作用域:整个ServletContext创建一个实例
    String SCOPE_APPLICATION = "application";
}

// 使用示例
@Component
@Scope("singleton")  // 默认就是singleton
public class SingletonBean { }

@Component
@Scope("prototype")
public class PrototypeBean { }

@RestController
public class WebController {
    @Autowired
    private UserService userService;  // singleton
    
    @Autowired
    private ApplicationContext context;
    
    @GetMapping("/test")
    public String test() {
        // 每次请求都获取新的prototype实例
        PrototypeBean bean = context.getBean(PrototypeBean.class);
        return "OK";
    }
}

项目场景:在电商系统中,购物车使用session作用域,每个用户有自己的购物车实例;商品详情使用prototype作用域,每次查看都创建新对象避免数据污染;服务类使用singleton作用域,无状态设计实现高性能并发处理。

2. 作用域详细对比

一句话原理:不同作用域对应不同生命周期并发模型:singleton线程安全需无状态设计,prototype每次新建适合有状态对象,request/session在Web环境中按请求/会话隔离数据。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/**
 * 五大作用域源码实现详解
 */
public class BeanScopeDetail {
    
    // 1. singleton(单例) - 默认作用域
    @Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
    @Component
    public class SingletonService {
        // 特点:容器启动时创建(可配置懒加载),容器关闭时销毁
        // 注意:必须无状态设计,否则线程不安全
        
        private int state;  // ❌ 错误:成员变量会共享
        
        public void process() {
            // ✅ 正确:只使用局部变量和方法参数
            int localVar = 0;
        }
    }
    
    // 2. prototype(原型) - 每次获取新实例
    @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    @Component
    public class PrototypeTask {
        private String taskId;  // ✅ 可以有状态,因为每次新建
        
        public void setTaskId(String taskId) {
            this.taskId = taskId;
        }
        
        public void execute() {
            System.out.println("执行任务:" + taskId);
        }
        
        // 注意:prototype Bean的销毁方法不会被Spring调用
        @PreDestroy
        public void cleanup() {
            // 这个方法不会被调用!
        }
    }
    
    // 3. request(请求) - 每个HTTP请求一个实例
    @Scope(value = WebApplicationContext.SCOPE_REQUEST, 
           proxyMode = ScopedProxyMode.TARGET_CLASS)  // 必须使用代理
    @Component
    public class RequestContext {
        private String requestId;
        private String userId;
        private String ip;
        
        public void init(HttpServletRequest request) {
            this.requestId = UUID.randomUUID().toString();
            this.userId = request.getParameter("userId");
            this.ip = request.getRemoteAddr();
        }
        
        // 同一个请求内共享这些数据
        public String getRequestId() { return requestId; }
    }
    
    // 4. session(会话) - 每个用户会话一个实例
    @Scope(value = WebApplicationContext.SCOPE_SESSION,
           proxyMode = ScopedProxyMode.TARGET_CLASS)
    @Component
    public class ShoppingCart {
        private List<CartItem> items = new ArrayList<>();
        private String sessionId;
        
        public void addItem(Product product, int count) {
            items.add(new CartItem(product, count));
        }
        
        public List<CartItem> getItems() {
            return items;
        }
        
        // 用户会话期间一直存在
    }
    
    // 5. application(应用) - 整个ServletContext一个实例
    @Scope(value = WebApplicationContext.SCOPE_APPLICATION,
           proxyMode = ScopedProxyMode.TARGET_CLASS)
    @Component
    public class ApplicationCounter {
        private AtomicLong counter = new AtomicLong(0);
        
        public long increment() {
            return counter.incrementAndGet();  // 所有用户共享
        }
        
        // 应用生命周期内存在
    }
}

项目场景:在微服务网关中,RequestContext使用request作用域存储请求链路ID,方便日志追踪;UserSession使用session作用域存储登录状态;ApiCounter使用application作用域统计总请求量;RestTemplate使用singleton作为无状态HTTP客户端。

3. 作用域依赖问题与解决方案

一句话原理:不同作用域的Bean注入时存在生命周期差异问题(如singleton注入prototype),需要通过代理模式(@Scope proxyMode)或方法注入(Lookup)解决,保证每次获取的都是正确作用域的实例。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
/**
 * 作用域依赖问题与解决方案
 */
public class ScopeDependencySolution {
    
    // 问题:singleton注入prototype
    @Component
    public class SingletonService {
        // ❌ 错误:构造函数只注入一次,导致每次都用同一个prototype实例
        @Autowired
        private PrototypeBean prototypeBean;
        
        public void usePrototype() {
            prototypeBean.doSomething();  // 永远是同一个对象
        }
    }
    
    // 解决方案1:使用代理模式(推荐)
    @Component
    public class ProxySolution {
        @Autowired
        private PrototypeBeanWithProxy prototypeBean;  // 注入的是代理对象
        
        public void usePrototype() {
            // 每次调用代理对象的方法,都会获取新的真实实例
            prototypeBean.doSomething();  // 每次都是新的
        }
    }
    
    @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE,
           proxyMode = ScopedProxyMode.TARGET_CLASS)  // 使用代理
    @Component
    public class PrototypeBeanWithProxy {
        private UUID id = UUID.randomUUID();  // 每次调用会不同
        
        public void doSomething() {
            System.out.println("实例ID:" + id);
        }
    }
    
    // 解决方案2:使用ObjectFactory(手动获取)
    @Component
    public class ObjectFactorySolution {
        @Autowired
        private ObjectFactory<PrototypeBean> prototypeFactory;  // 工厂
        
        public void usePrototype() {
            // 每次需要时从工厂获取新实例
            PrototypeBean bean = prototypeFactory.getObject();
            bean.doSomething();
        }
    }
    
    // 解决方案3:使用Lookup方法注入(抽象方法)
    @Component
    public abstract class LookupSolution {
        
        public void usePrototype() {
            PrototypeBean bean = createPrototypeBean();  // 每次调用抽象方法
            bean.doSomething();
        }
        
        @Lookup  // Spring会实现该方法,返回新的prototype实例
        protected abstract PrototypeBean createPrototypeBean();
    }
    
    // Web作用域也需要代理
    @RestController
    public class WebScopeController {
        
        @Autowired
        private RequestContext requestContext;  // request作用域必须用代理
        
        @Autowired
        private ShoppingCart shoppingCart;      // session作用域必须用代理
        
        @GetMapping("/cart/add")
        public String addToCart(@RequestParam String productId) {
            // 这里的shoppingCart是代理对象,实际获取当前会话的购物车
            shoppingCart.addItem(productId, 1);
            return "added";
        }
    }
}

项目场景:在在线教育系统中,CourseService是singleton,需要调用LearningProgressService(prototype,每个课程学习有不同的进度对象)。通过设置proxyMode,CourseService中注入的是代理对象,每次调用都能获取当前课程的正确进度实例,避免了状态混乱。

完整实战指南

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
/**
 * Bean作用域完整实战指南
 */
public class BeanScopeGuide {
    
    /**
     * 1. singleton作用域最佳实践
     */
    @Configuration
    public class SingletonBestPractice {
        
        @Bean
        @Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
        public DataSource dataSource() {
            // 数据库连接池是典型的singleton
            return new HikariDataSource();  // 整个应用共享一个连接池
        }
        
        @Bean
        @Lazy  // 懒加载,需要时才创建
        public HeavyResource heavyResource() {
            return new HeavyResource();  // 启动时不创建,第一次使用时创建
        }
        
        // 无状态服务类
        @Service
        public class UserService {  // 默认singleton
            // 不能有可修改的成员变量
            private final UserRepository repository;  // 注入的依赖也是singleton
            
            public UserService(UserRepository repository) {
                this.repository = repository;
            }
            
            public User findUser(String id) {
                return repository.findById(id);  // 只使用方法参数和返回值
            }
        }
    }
    
    /**
     * 2. prototype作用域最佳实践
     */
    @Configuration
    public class PrototypeBestPractice {
        
        @Bean
        @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
        public ReportGenerator reportGenerator(String type) {
            // 可以传入参数创建不同报表生成器
            return new ReportGenerator(type);
        }
        
        @Component
        @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
        public class BatchTask {
            private final String batchId;
            private final List<String> items;
            
            // 构造器注入,每次创建新的任务
            public BatchTask(String batchId, List<String> items) {
                this.batchId = batchId;
                this.items = new ArrayList<>(items);
            }
            
            public void execute() {
                // 执行批量任务
            }
        }
        
        // 使用prototype Bean的工厂
        @Service
        public class TaskProcessor {
            @Autowired
            private ApplicationContext context;
            
            public void processBatch(String batchId, List<String> items) {
                // 每次创建新的BatchTask实例
                BatchTask task = context.getBean(BatchTask.class, batchId, items);
                task.execute();
            }
        }
    }
    
    /**
     * 3. Web作用域最佳实践
     */
    @Configuration
    @EnableWebMvc
    public class WebScopeConfig {
        
        // 配置Web作用域
        @Bean
        @Scope(value = WebApplicationContext.SCOPE_REQUEST, 
               proxyMode = ScopedProxyMode.TARGET_CLASS)
        public RequestContext requestContext() {
            return new RequestContext();
        }
        
        @Bean
        @Scope(value = WebApplicationContext.SCOPE_SESSION,
               proxyMode = ScopedProxyMode.TARGET_CLASS)
        public ShoppingCart shoppingCart() {
            return new ShoppingCart();
        }
    }
    
    @RestController
    public class ShoppingController {
        @Autowired
        private ShoppingCart cart;  // session作用域
        
        @Autowired
        private RequestContext context;  // request作用域
        
        @Autowired
        private ProductService productService;  // singleton
        
        @PostMapping("/cart/add")
        public Result addToCart(@RequestBody AddRequest req) {
            // 记录请求日志
            log.info("请求ID:{},用户:{}", context.getRequestId(), context.getUserId());
            
            // 获取商品信息(singleton service)
            Product product = productService.findById(req.getProductId());
            
            // 添加到购物车(session)
            cart.addItem(product, req.getCount());
            
            return Result.success(cart.getItemCount());
        }
        
        @GetMapping("/cart/list")
        public List<CartItem> getCart() {
            return cart.getItems();  // 获取当前用户的购物车
        }
    }
    
    /**
     * 4. 作用域选择决策树
     */
    public class ScopeDecisionTree {
        
        // 1. 是否无状态?
        //    ├─ 是 → singleton
        //    └─ 否 → 转到2
        
        // 2. 状态是否与线程相关?
        //    ├─ 是 → prototype(每个任务新建)
        //    └─ 否 → 转到3
        
        // 3. 状态是否与HTTP请求相关?
        //    ├─ 是 → request
        //    └─ 否 → 转到4
        
        // 4. 状态是否与用户会话相关?
        //    ├─ 是 → session
        //    └─ 否 → 转到5
        
        // 5. 状态是否与应用全局相关?
        //    ├─ 是 → application
        //    └─ 否 → prototype
        
        // 典型案例:
        // - 数据库连接池:singleton(无状态)
        // - 邮件发送任务:prototype(每次新建)
        // - 请求跟踪ID:request(请求级)
        // - 购物车:session(用户级)
        // - 访问计数器:application(应用级)
    }
    
    /**
     * 5. 监控与调试
     */
    @Component
    public class ScopeMonitor {
        
        @Autowired
        private ApplicationContext context;
        
        public void printBeanInfo(String beanName) {
            // 获取Bean定义信息
            BeanDefinition bd = ((GenericApplicationContext) context)
                .getBeanDefinition(beanName);
            
            System.out.println("Bean名称:" + beanName);
            System.out.println("作用域:" + bd.getScope());
            System.out.println("是否懒加载:" + bd.isLazyInit());
            System.out.println("是否单例:" + bd.isSingleton());
            System.out.println("是否原型:" + bd.isPrototype());
        }
        
        // 监控不同作用域的Bean实例数
        public void monitorScopeCount() {
            // singleton:应该只有一个
            Map<String, SingletonService> singletons = 
                context.getBeansOfType(SingletonService.class);
            System.out.println("Singleton实例数:" + singletons.size());
            
            // prototype:每次获取计数增加
            PrototypeBean p1 = context.getBean(PrototypeBean.class);
            PrototypeBean p2 = context.getBean(PrototypeBean.class);
            System.out.println("原型Bean是否相同:" + (p1 == p2));  // false
        }
    }
}

/**
 * Bean作用域总结表
 * 
 * 作用域       生命周期               使用场景                   注意事项
 * singleton   容器启动到关闭          无状态Service、DAO、连接池   必须无状态
 * prototype   每次获取新实例           有状态Task、Command         Spring不管理销毁
 * request     HTTP请求开始到结束       请求上下文、跟踪ID          需要代理
 * session     HTTP会话开始到结束       购物车、用户信息            需要代理
 * application ServletContext生命周期   应用计数器、全局配置        需要代理
 * 
 * 选择原则:
 * 1. 无状态:singleton
 * 2. 有状态但独立:prototype
 * 3. 有状态且与请求相关:request
 * 4. 有状态且与用户相关:session
 * 5. 有状态且与应用相关:application
 */

// 面试金句
// "Bean作用域就像'物品的存放规则':
//  singleton是'公共图书馆的书'(一人借所有人看,但不能乱写乱画),
//  prototype是'超市购物袋'(每次新拿一个,用完就扔),
//  request是'医院的挂号单'(只看这次病),
//  session是'健身房储物柜'(你运动期间一直归你用),
//  application是'大楼前台登记簿'(永久保存)。
//  在电商系统中,我把用户service设计为singleton(无状态),
//  购物车设计为session(用户专有),请求日志设计为request(请求专属),
//  订单处理任务设计为prototype(每次新建)。
//  理解作用域的本质才能合理管理对象的生命周期和状态"

如何实现单例 Bean 中的原型 Bean 依赖?

1. 问题本质:生命周期不匹配

一句话原理:单例Bean在容器启动时创建一次,而原型Bean需要每次获取新实例,直接注入会导致单例Bean持有固定的原型Bean实例,无法实现"每次调用都是新实例"的需求,这是由Bean生命周期差异造成的经典问题。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
 * 问题复现:单例Bean注入原型Bean
 */
@Component
public class SingletonService {
    
    // ❌ 问题:这会在容器启动时注入一个固定的原型Bean实例
    @Autowired
    private PrototypeBean prototypeBean;
    
    public void doWork() {
        // 每次调用都是同一个prototypeBean实例,不是新的!
        prototypeBean.process();  
    }
}

@Scope("prototype")
@Component
public class PrototypeBean {
    private UUID instanceId = UUID.randomUUID();  // 每个实例有唯一ID
    
    public void process() {
        System.out.println("当前实例ID: " + instanceId);
        // 期望每次调用打印不同ID,但实际每次都相同
    }
}

// 测试代码
@RunWith(SpringRunner.class)
public class SingletonTest {
    @Autowired
    private SingletonService singleton;
    
    @Test
    public void test() {
        singleton.doWork();  // 打印 ID1
        singleton.doWork();  // 打印 ID1(相同!)
        singleton.doWork();  // 打印 ID1(还是相同!)
    }
}

项目场景:在订单处理系统中,OrderProcessor是单例(无状态服务),需要每次调用时创建新的TransactionLogger(原型,记录每次操作的独立日志)。如果直接在单例中注入原型,所有订单都会使用同一个日志实例,导致日志混乱。

2. 解决方案一:ApplicationContextAware(手动获取)

一句话原理:通过实现ApplicationContextAware接口,单例Bean持有容器引用,每次需要原型Bean时手动调用getBean()获取新实例,将控制权从自动注入转为主动获取。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
 * 方案1:ApplicationContextAware方式
 */
@Component
public class SingletonService implements ApplicationContextAware {
    
    private ApplicationContext applicationContext;
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;  // 持有容器引用
    }
    
    public void doWork() {
        // ✅ 每次手动获取新实例
        PrototypeBean prototypeBean = applicationContext.getBean(PrototypeBean.class);
        prototypeBean.process();
        
        // 也可以传入参数创建不同实例
        PrototypeBean task = applicationContext.getBean(
            PrototypeBean.class, "taskId", "userId");
    }
}

// 带参数的原型Bean
@Scope("prototype")
@Component
public class PrototypeBean {
    private String taskId;
    private String userId;
    
    public PrototypeBean(String taskId, String userId) {
        this.taskId = taskId;
        this.userId = userId;
    }
    
    @Autowired
    private LogService logService;  // 仍然可以自动注入其他依赖
}

// 测试结果
@Test
public void test() {
    singleton.doWork();  // 打印实例1
    singleton.doWork();  // 打印实例2(不同!)
    singleton.doWork();  // 打印实例3(不同!)
}

项目场景:在批处理系统中,BatchJobScheduler(单例)需要为每个任务创建独立的JobExecutor(原型,包含任务配置)。通过ApplicationContextAware,每次启动任务时获取新的JobExecutor,并传入任务ID等参数,实现了任务的完全隔离。

3. 解决方案二:ObjectFactory(延迟依赖查找)

一句话原理:Spring提供ObjectFactoryObjectProvider(推荐),作为依赖查找的抽象,单例Bean注入ObjectFactory<PrototypeBean>,每次调用getObject()获取新实例,比直接持有容器更优雅且解耦。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/**
 * 方案2:ObjectFactory方式(推荐)
 */
@Component
public class SingletonService {
    
    // ✅ 注入ObjectFactory,不是直接注入原型Bean
    @Autowired
    private ObjectFactory<PrototypeBean> prototypeBeanFactory;
    
    // 或者使用ObjectProvider(功能更丰富)
    @Autowired
    private ObjectProvider<PrototypeBean> prototypeBeanProvider;
    
    public void doWork() {
        // 方式1:获取新实例
        PrototypeBean bean1 = prototypeBeanFactory.getObject();
        bean1.process();
        
        // 方式2:带参数的实例(需要构造器支持)
        PrototypeBean bean2 = prototypeBeanProvider.getObject("task123", "user456");
        bean2.process();
        
        // 方式3:获取多个实例(如果有多个实现)
        PrototypeBean bean3 = prototypeBeanProvider.getIfAvailable();
        PrototypeBean bean4 = prototypeBeanProvider.getIfUnique();
    }
}

// ObjectFactory源码分析
public interface ObjectFactory<T> {
    T getObject();  // 返回新的或现有的Bean实例
}

public interface ObjectProvider<T> extends ObjectFactory<T> {
    T getObject(Object... args);  // 支持参数
    T getIfAvailable();
    T getIfUnique();
    Stream<T> stream();
}

// Spring内部实现
public class DefaultListableBeanFactory {
    
    // ObjectFactory实际上调用的是getBean方法
    public <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType) {
        return new BeanObjectProvider<T>() {
            @Override
            public T getObject() {
                return getBean(requiredType);  // 每次调用getBean
            }
        };
    }
}

项目场景:在消息处理系统中,MessageConsumer(单例)需要为每条消息创建新的MessageProcessor(原型,处理不同业务类型)。使用ObjectProvider,代码清晰且易于测试,同时支持根据消息类型获取不同实现。

4. 解决方案三:@Lookup注解(方法注入)

一句话原理:Spring提供@Lookup注解,标注在抽象方法或具体方法上,容器会动态生成该方法实现,每次调用都返回新的原型Bean实例,是最简洁、最Spring风格的解决方案。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
 * 方案3:@Lookup注解方式(最优雅)
 */
@Component
public abstract class SingletonService {  // 可以是抽象类
    
    public void doWork() {
        // 调用抽象方法,Spring会动态实现
        PrototypeBean bean = createPrototypeBean();
        bean.process();
        
        // 带参数版本
        PrototypeBean bean2 = createPrototypeBeanWithArgs("task123");
        bean2.process();
    }
    
    // ✅ @Lookup标注的方法,返回原型Bean
    @Lookup
    protected abstract PrototypeBean createPrototypeBean();
    
    // 支持参数的方法注入
    @Lookup
    protected abstract PrototypeBean createPrototypeBeanWithArgs(String taskId);
    
    // 也可以不是抽象方法(覆盖用)
    @Lookup
    protected PrototypeBean createPrototypeBeanDefault() {
        return null;  // Spring会覆盖实现
    }
}

// @Lookup注解源码
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Lookup {
    String value() default "";  // 可以指定Bean名称
}

// Spring生成的实现类(伪代码)
public class SingletonService$$Enhancer extends SingletonService {
    @Override
    protected PrototypeBean createPrototypeBean() {
        // Spring动态生成的代码
        return applicationContext.getBean(PrototypeBean.class);
    }
}

项目场景:在Web应用中,UserController(单例)需要为每个请求创建新的RequestContext(原型)。使用@Lookup注入,代码最简洁,且与Spring框架完美集成,不需要额外配置。同时支持单元测试时手动mock该方法。

完整对比与实战指南

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/**
 * 三种解决方案完整对比
 */
public class SolutionsCompleteGuide {
    
    /**
     * 1. 三种方案对比表
     */
    public class Comparison {
        // 方案          优点                     缺点                    适用场景
        // Aware       简单直观,功能强大         侵入性强,耦合容器        遗留系统改造
        // ObjectFactory 解耦,功能丰富            比Lookup稍显复杂         需要灵活获取多个实例
        // Lookup       最优雅,符合DI思想        方法必须是抽象/可覆盖      新开发项目,追求简洁
    }
    
    /**
     * 2. 实战:完整示例
     */
    @Configuration
    public class CompleteExample {
        
        // 场景:订单处理系统
        // - OrderProcessor:单例服务
        // - TransactionRecorder:原型,记录每笔交易
        
        @Bean
        @Scope("prototype")
        public TransactionRecorder transactionRecorder(String orderId, String userId) {
            return new TransactionRecorder(orderId, userId);
        }
        
        @Service
        public static class OrderProcessor {
            
            // 方案A:使用ObjectProvider
            private final ObjectProvider<TransactionRecorder> recorderProvider;
            
            // 方案B:使用@Lookup(与构造器注入冲突,此处用ObjectProvider演示)
            public OrderProcessor(ObjectProvider<TransactionRecorder> recorderProvider) {
                this.recorderProvider = recorderProvider;
            }
            
            @Transactional
            public void processOrder(Order order) {
                // 1. 前置处理
                
                // 2. 创建新的事务记录器(带参数)
                TransactionRecorder recorder = recorderProvider.getObject(
                    order.getId(), order.getUserId());
                
                // 3. 记录开始
                recorder.recordStart();
                
                try {
                    // 业务逻辑
                    doBusiness(order);
                    
                    // 4. 记录成功
                    recorder.recordSuccess();
                } catch (Exception e) {
                    // 5. 记录失败
                    recorder.recordFailure(e);
                    throw e;
                }
            }
            
            private void doBusiness(Order order) {
                // 核心业务
            }
        }
        
        // 原型Bean
        public static class TransactionRecorder {
            private final String orderId;
            private final String userId;
            private final Date startTime;
            
            @Autowired
            private AuditService auditService;  // 仍然可以自动注入
            
            public TransactionRecorder(String orderId, String userId) {
                this.orderId = orderId;
                this.userId = userId;
                this.startTime = new Date();
            }
            
            public void recordStart() {
                auditService.log("交易开始", orderId, userId);
            }
            
            public void recordSuccess() {
                long cost = new Date().getTime() - startTime.getTime();
                auditService.log("交易成功", orderId, userId, cost);
            }
            
            public void recordFailure(Exception e) {
                auditService.log("交易失败", orderId, userId, e.getMessage());
            }
        }
    }
    
    /**
     * 3. 单元测试技巧
     */
    @RunWith(MockitoJUnitRunner.class)
    public class UnitTest {
        
        @Test
        public void testLookupMethod() {
            // 测试@Lookup方式
            SingletonService service = mock(SingletonService.class);
            
            // 当调用createPrototypeBean时,返回mock对象
            PrototypeBean mockBean = mock(PrototypeBean.class);
            when(service.createPrototypeBean()).thenReturn(mockBean);
            
            // 调用业务方法
            service.doWork();
            
            // 验证
            verify(mockBean).process();
        }
        
        @Test
        public void testObjectFactory() {
            // 测试ObjectFactory方式
            ObjectFactory<PrototypeBean> factory = mock(ObjectFactory.class);
            SingletonService service = new SingletonService(factory);
            
            PrototypeBean mockBean = mock(PrototypeBean.class);
            when(factory.getObject()).thenReturn(mockBean);
            
            service.doWork();
            verify(mockBean).process();
        }
    }
    
    /**
     * 4. 高级场景:多实例选择
     */
    @Service
    public class AdvancedService {
        
        @Autowired
        private ObjectProvider<List<Handler>> handlersProvider;  // 注入所有Handler
        
        public void processWithStrategy(String type) {
            // 根据类型选择不同的原型Bean
            List<Handler> handlers = handlersProvider.getObject();
            
            Handler handler = handlers.stream()
                .filter(h -> h.supports(type))
                .findFirst()
                .orElseThrow();
            
            handler.handle();
        }
    }
    
    /**
     * 5. 性能考虑
     */
    public class PerformanceConsideration {
        // 每次getBean()都有一定开销(但很小)
        // 优化策略:
        // - 避免在循环中频繁获取(可批量获取)
        // - 考虑缓存一些无状态的原型Bean
        // - 使用ThreadLocal做进一步优化
        
        // 性能对比(理论值):
        // new对象: 5-10ns
        // getBean: 500-1000ns (慢约100倍,但仍在微秒级)
        // 对于大多数业务场景,可忽略不计
    }
}

/**
 * 解决方案总结表
 * 
 * 方案           代码量   侵入性   灵活性   测试难度   推荐度
 * Aware          中      高      中      低       ⭐⭐
 * ObjectFactory  中      低      高      高       ⭐⭐⭐
 * Lookup         少      低      中      高       ⭐⭐⭐⭐
 * 
 * 选择建议:
 * - 新项目:优先@Lookup,代码最优雅
 * - 需要参数化:ObjectProvider(支持getObject(args))
 * - 遗留系统改造:Aware方式简单直接
 * - 需要复杂条件获取:ObjectProvider(stream、ifAvailable)
 */

// 面试金句
// "单例Bean依赖原型Bean就像'固定餐厅'需要'一次性餐具':
//  直接注入原型Bean相当于餐厅开业时把餐具固定下来(只创建一次),
//  结果每次客人用的都是同一套餐具(问题!)。
//  解决方案就像三种'餐具获取方式':
//  Aware是'餐厅老板自己去仓库拿'(手动获取),
//  ObjectFactory是'雇个服务员专职取餐具'(延迟工厂),
//  Lookup是'设置个传送带,需要时自动送过来'(方法注入)。
//  在支付系统中,我用@Lookup让单例的订单处理器每次都能拿到新的交易日志对象,
//  代码简洁又符合Spring风格每个订单都有独立的日志记录"

源码原理

Spring IOC 容器的启动流程是怎样的?(从 AnnotationConfigApplicationContext 的 refresh() 方法讲起)

1. refresh()方法总览:容器启动的12个关键步骤

一句话原理:Spring IOC容器的启动流程由AbstractApplicationContext.refresh()方法定义,它是一个模板方法,包含了12个核心步骤,从BeanFactory的创建、预处理,到Bean的加载、注册,再到最后的完成刷新,完整构建了整个IOC容器。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// AbstractApplicationContext.refresh() - IOC容器启动的总入口
@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 1. 准备刷新:设置启动时间、激活状态,初始化属性源
        prepareRefresh();
        
        // 2. 获取BeanFactory:创建或获取已有的BeanFactory
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        
        // 3. 准备BeanFactory:配置类加载器、表达式解析器、注册后处理器等
        prepareBeanFactory(beanFactory);
        
        try {
            // 4. 后处理BeanFactory:允许子类在BeanFactory创建后做额外配置
            postProcessBeanFactory(beanFactory);
            
            // 5. 调用BeanFactory后处理器:解析配置类,扫描包,注册Bean定义
            invokeBeanFactoryPostProcessors(beanFactory);
            
            // 6. 注册Bean后处理器:注册拦截Bean创建的处理器
            registerBeanPostProcessors(beanFactory);
            
            // 7. 初始化消息源:国际化支持
            initMessageSource();
            
            // 8. 初始化事件广播器:用于事件发布和监听
            initApplicationEventMulticaster();
            
            // 9. 刷新子类容器:模板方法,留给子类实现(如Spring MVC)
            onRefresh();
            
            // 10. 注册监听器:将ApplicationListener注册到广播器
            registerListeners();
            
            // 11. 初始化所有非懒加载的单例Bean
            finishBeanFactoryInitialization(beanFactory);
            
            // 12. 完成刷新:清理缓存,发布刷新事件
            finishRefresh();
            
        } catch (BeansException ex) {
            // 异常处理:销毁已创建的Bean
            destroyBeans();
            cancelRefresh(ex);
            throw ex;
        } finally {
            // 重置Spring内核中的公共缓存
            resetCommonCaches();
        }
    }
}

项目场景:在微服务启动时,通过new AnnotationConfigApplicationContext("com.example")触发refresh()流程,12个步骤环环相扣,最终完成所有Bean的创建和依赖注入,服务开始对外提供接口。

2. 关键步骤深度解析(1-6步:准备阶段)

2.1 prepareRefresh() - 准备刷新

一句话原理:设置容器启动时间激活状态,初始化环境属性(如系统变量、JVM属性),并准备监听器集合,为后续步骤做好准备。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// AbstractApplicationContext.prepareRefresh()
protected void prepareRefresh() {
    // 记录启动时间
    this.startupDate = System.currentTimeMillis();
    // 设置容器为激活状态
    this.closed.set(false);
    this.active.set(true);
    
    // 初始化属性源(留给子类实现)
    initPropertySources();
    
    // 验证必要属性是否存在
    getEnvironment().validateRequiredProperties();
    
    // 初始化早期事件集合(用于监听器未注册前的事件)
    this.earlyApplicationEvents = new LinkedHashSet<>();
}

2.2 obtainFreshBeanFactory() - 获取BeanFactory

一句话原理:如果是首次创建则创建新的BeanFactory,并加载Bean定义;如果是已存在则刷新并返回。对于AnnotationConfigApplicationContext,这一步会创建DefaultListableBeanFactory

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// AbstractApplicationContext.obtainFreshBeanFactory()
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    // 刷新BeanFactory(模板方法)
    refreshBeanFactory();
    // 获取BeanFactory
    return getBeanFactory();
}

// GenericApplicationContext.refreshBeanFactory()(AnnotationConfigApplicationContext的父类)
@Override
protected final void refreshBeanFactory() throws IllegalStateException {
    if (!this.refreshed.compareAndSet(false, true)) {
        throw new IllegalStateException("GenericApplicationContext does not support multiple refresh attempts");
    }
    // 设置BeanFactory的序列化ID
    this.beanFactory.setSerializationId(getId());
}

// 默认BeanFactory是DefaultListableBeanFactory
// 它包含了Bean定义Map单例对象Map别名Map等核心数据结构

2.3 prepareBeanFactory() - 准备BeanFactory

一句话原理:为BeanFactory配置类加载器表达式解析器属性编辑器,并注册一些特殊的Bean后处理器,如ApplicationContextAwareProcessor,同时忽略特定接口的自动装配

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// AbstractApplicationContext.prepareBeanFactory()
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // 设置类加载器(使用当前线程的上下文类加载器)
    beanFactory.setBeanClassLoader(getClassLoader());
    
    // 设置表达式解析器(用于解析@Value中的#{...})
    beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver());
    
    // 设置属性编辑器(用于类型转换)
    beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
    
    // 添加Bean后处理器:处理Aware接口回调
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
    
    // 忽略自动装配的接口(这些接口通过Aware回调注入,而不是自动装配)
    beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
    beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
    beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
    beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
    
    // 注册特殊类型的自动装配候选
    beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    beanFactory.registerResolvableDependency(ApplicationContext.class, this);
    
    // 注册早期后处理器:用于监听ApplicationEvent
    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
    
    // 如果存在LoadTimeWeaver,注册对应的后处理器(AOP相关)
    if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }
}

2.4 postProcessBeanFactory() - 后处理BeanFactory

一句话原理模板方法,允许子类在BeanFactory创建完成后,所有Bean定义加载前,对BeanFactory进行自定义修改。例如Spring Boot的EmbeddedWebApplicationContext会在这里注册Web相关的Scope。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// AbstractApplicationContext.postProcessBeanFactory() - 默认空实现
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // 留给子类覆盖
}

// GenericWebApplicationContext中的实现
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // 注册Web相关的Scope(request、session、application)
    beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
    beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope());
    beanFactory.registerScope(WebApplicationContext.SCOPE_APPLICATION, new ServletContextScope(this.servletContext));
    
    // 注册其他Web相关配置
    webApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
}

2.5 invokeBeanFactoryPostProcessors() - 调用BeanFactory后处理器

一句话原理:这是最关键的步骤之一,调用所有BeanFactoryPostProcessor,包括处理配置类(如@Configuration)、扫描包(@ComponentScan)、解析@Bean方法注册Bean定义,此时Bean定义开始被加载到容器。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// AbstractApplicationContext.invokeBeanFactoryPostProcessors()
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    // 委托给PostProcessorRegistrationDelegate处理
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
}

// PostProcessorRegistrationDelegate的核心逻辑
public static void invokeBeanFactoryPostProcessors(
        ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
    
    // 1. 优先处理BeanDefinitionRegistryPostProcessor(可以注册更多Bean定义)
    Set<String> processedBeans = new HashSet<>();
    
    // 处理所有BeanDefinitionRegistryPostProcessor(包括从容器中获取的)
    invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
    
    // 2. 处理普通的BeanFactoryPostProcessor
    invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
    
    // 3. 处理从容器中获取的其他BeanFactoryPostProcessor
    String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
    
    // 分类处理:PriorityOrdered、Ordered、普通
    sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
    invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
    
    // 重点:ConfigurationClassPostProcessor在这里执行
    // - 解析@Configuration类
    // - 执行@ComponentScan扫描包
    // - 处理@Import注解
    // - 解析@Bean方法
}

项目场景:当使用new AnnotationConfigApplicationContext(AppConfig.class)时,ConfigurationClassPostProcessor被调用,解析AppConfig上的@ComponentScan注解,扫描指定包下的所有@Component类,将它们注册为Bean定义。这就是为什么只需要配置一个配置类,就能自动加载所有Bean的原因。

3. 关键步骤深度解析(7-12步:初始化阶段)

3.1 registerBeanPostProcessors() - 注册Bean后处理器

一句话原理:查找并注册所有BeanPostProcessor,它们将在Bean初始化前后被调用,用于实现AOP代理属性填充自定义初始化逻辑等功能,按优先级顺序注册。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// AbstractApplicationContext.registerBeanPostProcessors()
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    // 委托给PostProcessorRegistrationDelegate
    PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}

// PostProcessorRegistrationDelegate.registerBeanPostProcessors()
public static void registerBeanPostProcessors(
        ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
    
    // 获取所有BeanPostProcessor(包括容器默认的和用户定义的)
    String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
    
    // 注册BeanPostProcessorChecker(用于记录未处理完就创建Bean的警告)
    int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
    beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
    
    // 1. 注册实现了PriorityOrdered的BeanPostProcessor
    List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
    for (String ppName : postProcessorNames) {
        if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
            BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
            priorityOrderedPostProcessors.add(pp);
        }
    }
    sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
    registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
    
    // 2. 注册实现了Ordered的BeanPostProcessor
    // 3. 注册普通的BeanPostProcessor
    
    // 4. 重新注册ApplicationListenerDetector(确保在最后)
    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}

3.2 initMessageSource() - 初始化消息源

一句话原理:初始化国际化相关的MessageSource,如果没有自定义,则注册一个DelegatingMessageSource作为委托,用于支持国际化消息解析。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// AbstractApplicationContext.initMessageSource()
protected void initMessageSource() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    
    // 检查是否有自定义的MessageSource
    if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
        // 使用自定义的MessageSource
        this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
        // 设置父MessageSource(如果有)
        if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
            HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
            if (hms.getParentMessageSource() == null) {
                hms.setParentMessageSource(getInternalParentMessageSource());
            }
        }
    } else {
        // 没有自定义,注册一个空的MessageSource
        DelegatingMessageSource dms = new DelegatingMessageSource();
        dms.setParentMessageSource(getInternalParentMessageSource());
        this.messageSource = dms;
        beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
    }
}

3.3 initApplicationEventMulticaster() - 初始化事件广播器

一句话原理:初始化事件广播器ApplicationEventMulticaster,如果没有自定义,则创建SimpleApplicationEventMulticaster,负责将ApplicationEvent广播给所有注册的ApplicationListener。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// AbstractApplicationContext.initApplicationEventMulticaster()
protected void initApplicationEventMulticaster() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    
    // 检查是否有自定义的事件广播器
    if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
        // 使用自定义的
        this.applicationEventMulticaster = beanFactory.getBean(
            APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
    } else {
        // 创建默认的SimpleApplicationEventMulticaster
        this.applicationEventMulticaster = new SimpleApplicationEventMulticaster();
        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
    }
}

3.4 onRefresh() - 刷新子类容器

一句话原理模板方法,留给子类在Bean初始化前做特殊处理。例如Spring MVC会在这里初始化DispatcherServlet的Web容器,如TomcatJetty

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// AbstractApplicationContext.onRefresh() - 默认空实现
protected void onRefresh() throws BeansException {
    // 留给子类覆盖
}

// EmbeddedWebApplicationContext中的实现(Spring Boot)
@Override
protected void onRefresh() {
    super.onRefresh();
    try {
        // 创建并启动嵌入式Web容器(如Tomcat)
        createEmbeddedServletContainer();
    } catch (Throwable ex) {
        throw new ApplicationContextException("Unable to start embedded container", ex);
    }
}

// ServletWebServerApplicationContext(Spring Boot 2.x)
@Override
protected void onRefresh() {
    super.onRefresh();
    try {
        // 创建Web服务器
        createWebServer();
    } catch (Throwable ex) {
        throw new ApplicationContextException("Unable to start web server", ex);
    }
}

3.5 registerListeners() - 注册监听器

一句话原理:将容器中所有ApplicationListener注册到事件广播器,并发布早期事件(在监听器注册前发生的事件)。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// AbstractApplicationContext.registerListeners()
protected void registerListeners() {
    // 1. 注册静态指定的监听器(通过addApplicationListener添加的)
    for (ApplicationListener<?> listener : getApplicationListeners()) {
        getApplicationEventMulticaster().addApplicationListener(listener);
    }
    
    // 2. 从容器中获取所有ApplicationListener类型的Bean并注册
    String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
    for (String listenerBeanName : listenerBeanNames) {
        getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
    }
    
    // 3. 发布早期事件(在Bean初始化前发生的事件)
    Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
    this.earlyApplicationEvents = null;
    if (earlyEventsToProcess != null) {
        for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
            getApplicationEventMulticaster().multicastEvent(earlyEvent);
        }
    }
}

3.6 finishBeanFactoryInitialization() - 初始化单例Bean

一句话原理:这是最核心的步骤,初始化所有非懒加载的单例Bean。Spring开始实例化填充属性初始化(包括BeanPostProcessor的处理)所有单例Bean,完成整个IOC容器的核心功能。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// AbstractApplicationContext.finishBeanFactoryInitialization()
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    // 1. 设置类型转换器(如果有)
    if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
            beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
        beanFactory.setConversionService(
                beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
    }
    
    // 2. 注册默认的嵌入值解析器(用于解析@Value中的${...})
    if (!beanFactory.hasEmbeddedValueResolver()) {
        beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
    }
    
    // 3. 初始化LoadTimeWeaver(AOP相关)
    String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
    for (String weaverAwareName : weaverAwareNames) {
        getBean(weaverAwareName);
    }
    
    // 4. 停止使用临时类加载器
    beanFactory.setTempClassLoader(null);
    
    // 5. 冻结配置(不允许再修改Bean定义)
    beanFactory.freezeConfiguration();
    
    // 6. 初始化所有非懒加载的单例Bean
    beanFactory.preInstantiateSingletons();
}

// DefaultListableBeanFactory.preInstantiateSingletons()
@Override
public void preInstantiateSingletons() throws BeansException {
    // 获取所有Bean定义名称
    List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
    
    // 遍历所有Bean定义
    for (String beanName : beanNames) {
        BeanDefinition bd = getBeanDefinition(beanName);
        // 如果是非懒加载的单例Bean
        if (bd.isSingleton() && !bd.isLazyInit()) {
            // 如果是FactoryBean,特殊处理
            if (isFactoryBean(beanName)) {
                Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
                if (bean instanceof FactoryBean) {
                    FactoryBean<?> factory = (FactoryBean<?>) bean;
                    if (factory.isEagerInit()) {
                        getBean(beanName);
                    }
                }
            } else {
                // 普通Bean,直接初始化
                getBean(beanName);  // 这里触发Bean的创建
            }
        }
    }
    
    // 所有单例Bean初始化完成后,触发SmartInitializingSingleton的回调
    for (String beanName : beanNames) {
        Object singletonInstance = getSingleton(beanName);
        if (singletonInstance instanceof SmartInitializingSingleton) {
            ((SmartInitializingSingleton) singletonInstance).afterSingletonsInstantiated();
        }
    }
}

3.7 finishRefresh() - 完成刷新

一句话原理:最后一步,清理资源缓存初始化生命周期处理器发布ContextRefreshedEvent事件,通知所有监听器容器已启动完成。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// AbstractApplicationContext.finishRefresh()
protected void finishRefresh() {
    // 1. 清理上下文级别的资源缓存
    clearResourceCaches();
    
    // 2. 初始化生命周期处理器
    initLifecycleProcessor();
    
    // 3. 调用生命周期处理器的onRefresh()
    getLifecycleProcessor().onRefresh();
    
    // 4. 发布ContextRefreshedEvent事件
    publishEvent(new ContextRefreshedEvent(this));
    
    // 5. 参与LiveBeansView(用于JMX监控)
    LiveBeansView.registerApplicationContext(this);
}

// 初始化生命周期处理器
protected void initLifecycleProcessor() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    // 检查是否有自定义的LifecycleProcessor
    if (beanFactory.containsLocalBean(LIFECYCLE_PROCESSOR_BEAN_NAME)) {
        this.lifecycleProcessor = beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class);
    } else {
        // 创建默认的DefaultLifecycleProcessor
        DefaultLifecycleProcessor defaultProcessor = new DefaultLifecycleProcessor();
        defaultProcessor.setBeanFactory(beanFactory);
        this.lifecycleProcessor = defaultProcessor;
        beanFactory.registerSingleton(LIFECYCLE_PROCESSOR_BEAN_NAME, this.lifecycleProcessor);
    }
}

完整流程总结与实战意义

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/**
 * Spring IOC容器启动流程总结
 */
public class RefreshFlowSummary {
    
    // 12个步骤速记口诀:
    // "准取准后,调用注消,消刷监初完" 
    // 1准(prepareRefresh) 2取(obtainFreshBeanFactory) 3准(prepareBeanFactory) 
    // 4后(postProcessBeanFactory) 5调(invokeBeanFactoryPostProcessors) 
    // 6注(registerBeanPostProcessors) 7消(initMessageSource) 
    // 8刷(initApplicationEventMulticaster) 9监(onRefresh) 
    // 10初(registerListeners) 11完(finishBeanFactoryInitialization) 12成(finishRefresh)
    
    // 关键时间点:
    // - 步骤1-4:准备工作,容器还没开始创建Bean
    // - 步骤5:配置解析,Bean定义加载完成
    // - 步骤6-10:基础设施初始化
    // - 步骤11:所有单例Bean创建完成(最耗时)
    // - 步骤12:容器启动完成,发布事件
    
    // 实战意义:
    public class PracticalSignificance {
        
        // 1. 理解Bean生命周期
        // - BeanPostProcessor在步骤6注册,在步骤11生效
        // - InitializingBean在步骤11的getBean()中调用
        
        // 2. 理解扩展点
        // - BeanFactoryPostProcessor在步骤5执行(可修改Bean定义)
        // - BeanPostProcessor在步骤11执行(可修改Bean实例)
        // - ApplicationListener在步骤12收到ContextRefreshedEvent
        
        // 3. 性能优化
        // - 步骤11最耗时,可考虑懒加载优化启动速度
        // - BeanPostProcessor数量影响每个Bean的创建速度
        
        // 4. 问题排查
        // - 启动慢:检查步骤11中哪些Bean初始化慢
        // - 循环依赖:发生在步骤11的getBean()过程中
        // - 配置不生效:检查步骤5是否执行了相关后处理器
    }
}

/**
 * refresh()流程总结表
 * 
 * 步骤                    主要工作                        执行时机
 * prepareRefresh         初始化属性源,验证必要属性        最早
 * obtainFreshBeanFactory  获取或创建BeanFactory            第二步
 * prepareBeanFactory      配置BeanFactory的基础设施         第三步
 * postProcessBeanFactory  子类扩展点                       第四步
 * invokeBeanFactoryPostProcessors 解析配置,扫描包,注册Bean定义 第五步
 * registerBeanPostProcessors 注册Bean后处理器              第六步
 * initMessageSource       初始化国际化组件                 第七步
 * initApplicationEventMulticaster 初始化事件广播器          第八步
 * onRefresh               子类容器初始化(如Web容器)       第九步
 * registerListeners       注册事件监听器                   第十步
 * finishBeanFactoryInitialization 初始化所有单例Bean       第十一步
 * finishRefresh           清理缓存,发布刷新事件            第十二步
 */

// 面试金句
// "Spring的refresh()方法就像'装修新房'的12道工序:
//  先'通电通水'(prepareRefresh准备环境),
//  再'准备工具间'(obtainFreshBeanFactory创建工厂),
//  然后'设计电路图'(invokeBeanFactoryPostProcessors解析配置),
//  接着'安装开关插座'(registerBeanPostProcessors注册后处理器),
//  最核心的是'家具进场'(finishBeanFactoryInitialization创建Bean),
//  最后'开灯庆祝'(finishRefresh发布事件)。
//  理解这个流程,就能明白为什么有些扩展点在某个时机生效,
//  为什么循环依赖只能在某些条件下解决,
//  为什么懒加载能提升启动速度。
//  在我优化微服务启动速度时,就是通过分析这个流程,
//  发现某个第三方Bean初始化耗时过长,改为懒加载后,
//  启动时间从90秒降到了30秒"

BeanFactory 和 FactoryBean 的区别?

1. 核心概念区别

一句话原理BeanFactory是Spring IOC容器的顶级接口,负责Bean的创建、配置和管理;而FactoryBean是一个特殊的Bean,本身是一个工厂,用于生产其他Bean实例,两者是"容器"与"工厂"的本质区别。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/**
 * BeanFactory:IOC容器核心接口
 */
public interface BeanFactory {
    // 获取Bean(按名称)
    Object getBean(String name) throws BeansException;
    
    // 获取Bean(按类型)
    <T> T getBean(Class<T> requiredType) throws BeansException;
    
    // 判断是否包含Bean
    boolean containsBean(String name);
    
    // 判断Bean是否是单例
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
    
    // 获取Bean的类型
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;
    
    // 别名处理
    String[] getAliases(String name);
}

/**
 * FactoryBean:生产复杂Bean的工厂接口
 */
public interface FactoryBean<T> {
    // 获取FactoryBean生产的Bean实例
    T getObject() throws Exception;
    
    // 获取FactoryBean生产的Bean类型
    Class<?> getObjectType();
    
    // 判断生产的Bean是否是单例
    boolean isSingleton();
}

// 使用示例对比
@Autowired
private BeanFactory beanFactory;  // 注入容器本身

@Autowired
private MyFactoryBean myFactoryBean;  // 注入FactoryBean本身(注意:不是它生产的Bean)

@Autowired
private MyComplexObject myComplexObject;  // 注入FactoryBean生产的Bean

项目场景:在集成第三方框架时,经常需要创建复杂的Bean(如SqlSessionFactory、KafkaProducer)。直接配置非常繁琐,通过实现FactoryBean,将复杂创建逻辑封装在getObject()中,容器调用FactoryBean获取真正的Bean实例,简化了配置。

2. BeanFactory深度解析

一句话原理:BeanFactory是Spring IOC容器的根接口,定义了容器的基本行为,其实现类(如DefaultListableBeanFactory)负责BeanDefinition的注册依赖解析生命周期管理,是Spring的核心基础设施

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/**
 * BeanFactory继承体系
 */
public interface BeanFactory {  // 顶级接口
    // 基本容器功能
}

public interface HierarchicalBeanFactory extends BeanFactory {  
    // 支持父子容器
    BeanFactory getParentBeanFactory();
}

public interface ListableBeanFactory extends BeanFactory {  
    // 支持批量获取Bean
    int getBeanDefinitionCount();
    String[] getBeanDefinitionNames();
    <T> Map<String, T> getBeansOfType(Class<T> type);
}

public interface AutowireCapableBeanFactory extends BeanFactory {  
    // 支持自动装配
    Object createBean(Class<?> beanClass);
    void autowireBean(Object existingBean);
    Object initializeBean(Object existingBean, String beanName);
}

public class DefaultListableBeanFactory 
    extends AbstractAutowireCapableBeanFactory 
    implements ConfigurableListableBeanFactory, BeanDefinitionRegistry {
    
    // Bean定义存储
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    
    // 单例Bean缓存
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
    
    // 早期单例Bean缓存(解决循环依赖)
    private final Map<String, Object> earlySingletonObjects = new HashMap<>();
    
    // 单例工厂缓存
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>();
    
    @Override
    public Object getBean(String name) throws BeansException {
        // 1. 从缓存获取
        Object sharedInstance = getSingleton(name);
        if (sharedInstance != null) {
            // 如果是FactoryBean,获取它生产的Bean
            return getObjectForBeanInstance(sharedInstance, name);
        }
        
        // 2. 缓存没有,创建Bean
        return createBean(name);
    }
}

项目场景:在自定义Spring扩展时,经常需要直接操作BeanFactory。例如实现动态注册Bean:通过DefaultListableBeanFactory.registerBeanDefinition()在运行时向容器注册新的Bean,实现插件的热加载功能。

3. FactoryBean深度解析

一句话原理:FactoryBean是Spring提供的创建复杂Bean的工厂接口,当Bean的创建过程比较复杂(如需要读取配置、建立连接、代理生成)时,通过实现FactoryBean封装创建逻辑,容器会自动识别并使用它生产Bean。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/**
 * FactoryBean经典实现:MyBatis的SqlSessionFactoryBean
 */
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean {
    
    private DataSource dataSource;
    private String configLocation;
    private Resource[] mapperLocations;
    private SqlSessionFactory sqlSessionFactory;
    
    // 注入依赖
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    
    // 初始化完成后创建真正的SqlSessionFactory
    @Override
    public void afterPropertiesSet() throws Exception {
        // 复杂构建过程:读取配置、解析XML、创建Configuration
        this.sqlSessionFactory = buildSqlSessionFactory();
    }
    
    // 返回真正的SqlSessionFactory实例
    @Override
    public SqlSessionFactory getObject() throws Exception {
        return this.sqlSessionFactory;
    }
    
    @Override
    public Class<?> getObjectType() {
        return SqlSessionFactory.class;
    }
    
    @Override
    public boolean isSingleton() {
        return true;  // 生产的Bean是单例
    }
    
    private SqlSessionFactory buildSqlSessionFactory() throws Exception {
        // 复杂的构建逻辑
        XMLConfigBuilder parser = new XMLConfigBuilder(configLocation);
        Configuration config = parser.parse();
        // ... 其他配置
        return new DefaultSqlSessionFactory(config);
    }
}

// 容器中的表现
@Configuration
public class AppConfig {
    
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        factoryBean.setConfigLocation("classpath:mybatis-config.xml");
        return factoryBean;  // 注册的是FactoryBean本身
    }
}

@Service
public class UserService {
    
    @Autowired
    private SqlSessionFactory sqlSessionFactory;  // 这里注入的是FactoryBean生产的Bean
    
    @Autowired
    private SqlSessionFactoryBean factoryBean;  // 如果需要操作FactoryBean本身
    
    public void test() {
        // 获取FactoryBean本身的两种方式
        SqlSessionFactoryBean bean1 = (SqlSessionFactoryBean) applicationContext.getBean("&sqlSessionFactory");
        SqlSessionFactoryBean bean2 = applicationContext.getBean("&sqlSessionFactory", SqlSessionFactoryBean.class);
    }
}

4. 核心区别对照表

一句话原理:BeanFactory和FactoryBean的区别可以概括为”容器 vs 工厂"、"管理Bean vs 生产Bean"、"全局 vs 局部",在Spring源码中有明确的命名约定:&前缀用于获取FactoryBean本身。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/**
 * 核心区别详细对比
 */
public class DifferenceAnalysis {
    
    // 1. 命名约定:&前缀
    public class NamingConvention {
        // 在BeanFactory中,通过名称获取Bean时:
        // - "userService" -> 获取UserService实例
        // - "&userService" -> 获取UserService对应的FactoryBean(如果有)
        
        public void demo(ApplicationContext context) {
            // 获取FactoryBean生产的Bean
            SqlSessionFactory factory = context.getBean("sqlSessionFactory", SqlSessionFactory.class);
            
            // 获取FactoryBean本身(注意&前缀)
            SqlSessionFactoryBean factoryBean = context.getBean("&sqlSessionFactory", SqlSessionFactoryBean.class);
            
            // 源码实现:BeanFactoryUtils.transformedBeanName()处理&前缀
            String name = "&sqlSessionFactory";
            String beanName = (name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) ?
                              name.substring(BeanFactory.FACTORY_BEAN_PREFIX.length()) : name;
            // beanName = "sqlSessionFactory"
        }
    }
    
    // 2. 类型系统对比
    public class TypeSystem {
        // BeanFactory: 容器本身,类型固定
        BeanFactory factory = new DefaultListableBeanFactory();
        
        // FactoryBean: 生产不同类型Bean的工厂,类型可变
        FactoryBean<SqlSessionFactory> factoryBean1 = new SqlSessionFactoryBean();
        FactoryBean<DataSource> factoryBean2 = new DataSourceFactoryBean();
        FactoryBean<MongoClient> factoryBean3 = new MongoClientFactoryBean();
    }
    
    // 3. 生命周期对比
    public class Lifecycle {
        // BeanFactory: 容器的生命周期(启动、运行、销毁)
        // - 在refresh()时初始化
        // - 在close()时销毁
        
        // FactoryBean: 作为普通Bean,遵循Bean生命周期
        // - 实例化 → 属性注入 → afterPropertiesSet → getObject()被调用 → 销毁
    }
    
    // 4. 源码中的处理逻辑
    public class SourceCodeProcessing {
        
        // AbstractBeanFactory.getObjectForBeanInstance() 处理FactoryBean
        protected Object getObjectForBeanInstance(Object beanInstance, String name) {
            // 如果名称以&开头,直接返回FactoryBean本身
            if (BeanFactoryUtils.isFactoryDereference(name)) {
                return beanInstance;
            }
            
            // 如果beanInstance不是FactoryBean,直接返回
            if (!(beanInstance instanceof FactoryBean)) {
                return beanInstance;
            }
            
            // 从缓存获取FactoryBean生产的Bean
            Object object = getCachedObjectForFactoryBean(beanName);
            if (object == null) {
                FactoryBean<?> factoryBean = (FactoryBean<?>) beanInstance;
                // 调用FactoryBean.getObject()获取生产的Bean
                object = doGetObjectFromFactoryBean(factoryBean, beanName);
            }
            return object;
        }
    }
}

/**
 * 区别对照表
 * 
 * 维度            BeanFactory                          FactoryBean
 * 角色           IOC容器                              特殊的Bean
 * 功能           管理Bean的生命周期                     生产其他Bean
 * 接口           BeanFactory                           FactoryBean<T>
 * 获取方式        context.getBean()                     context.getBean("&beanName")
 * 常见实现        DefaultListableBeanFactory            SqlSessionFactoryBean, ProxyFactoryBean
 * 应用场景        容器基础                              复杂Bean的封装
 * 
 * 记忆口诀:
 * BeanFactory是'房东',管理所有租客(Bean)
 * FactoryBean是'二房东',自己也是租客,但还能帮别人租房
 */

完整实战指南

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/**
 * BeanFactory和FactoryBean实战应用
 */
public class PracticalGuide {
    
    /**
     * 1. 自定义FactoryBean实现
     */
    public static class MyServiceFactoryBean implements FactoryBean<MyService>, InitializingBean {
        
        private String configLocation;
        private MyService myService;
        
        // 注入配置
        public void setConfigLocation(String configLocation) {
            this.configLocation = configLocation;
        }
        
        // 初始化方法
        @Override
        public void afterPropertiesSet() throws Exception {
            // 模拟复杂的构建过程
            Properties props = new Properties();
            props.load(new FileInputStream(configLocation));
            
            // 根据配置创建不同的实现
            String type = props.getProperty("service.type");
            if ("remote".equals(type)) {
                this.myService = new RemoteService(props);
            } else {
                this.myService = new LocalService(props);
            }
        }
        
        @Override
        public MyService getObject() throws Exception {
            return myService;  // 返回已创建的实例
        }
        
        @Override
        public Class<?> getObjectType() {
            return MyService.class;
        }
        
        @Override
        public boolean isSingleton() {
            return true;  // 单例
        }
    }
    
    /**
     * 2. 配置和使用
     */
    @Configuration
    public class AppConfig {
        
        @Bean
        public MyServiceFactoryBean myService() {
            MyServiceFactoryBean factoryBean = new MyServiceFactoryBean();
            factoryBean.setConfigLocation("classpath:service.properties");
            return factoryBean;
        }
        
        @Bean
        public AnotherService anotherService(MyService myService) {  // 直接注入生产的Bean
            return new AnotherService(myService);
        }
    }
    
    /**
     * 3. Spring内置的FactoryBean示例
     */
    public class BuiltInFactoryBeans {
        
        // 1. ProxyFactoryBean - 创建AOP代理
        @Bean
        public ProxyFactoryBean userServiceProxy(UserService target) {
            ProxyFactoryBean proxy = new ProxyFactoryBean();
            proxy.setTarget(target);
            proxy.setInterceptorNames("transactionInterceptor");
            return proxy;
        }
        
        // 2. JndiObjectFactoryBean - 从JNDI查找对象
        @Bean
        public JndiObjectFactoryBean dataSource() {
            JndiObjectFactoryBean factory = new JndiObjectFactoryBean();
            factory.setJndiName("java:comp/env/jdbc/datasource");
            return factory;
        }
        
        // 3. LocalContainerEntityManagerFactoryBean - JPA实体管理器
        @Bean
        public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
            LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
            factory.setDataSource(dataSource());
            factory.setPackagesToScan("com.example.entity");
            return factory;
        }
    }
    
    /**
     * 4. 源码调试技巧
     */
    public class DebugTips {
        
        public void debugFactoryBean() {
            // 断点位置1: AbstractBeanFactory.getObjectForBeanInstance()
            // 观察如何处理&前缀和FactoryBean
            
            // 断点位置2: FactoryBeanRegistrySupport.doGetObjectFromFactoryBean()
            // 观察FactoryBean.getObject()的调用过程
            
            // 断点位置3: 自定义FactoryBean的getObject()方法
            // 观察实际对象的创建逻辑
        }
    }
    
    /**
     * 5. 面试常见问题
     */
    public class InterviewQuestions {
        
        // Q: 如果我想获取FactoryBean本身,怎么办?
        // A: 使用&前缀:context.getBean("&factoryBeanName")
        
        // Q: FactoryBean生产的Bean是否受容器生命周期管理?
        // A: 如果isSingleton()返回true,生产的Bean会被容器缓存和管理;
        //    如果返回false,每次getObject()返回新实例,容器不管理其生命周期
        
        // Q: FactoryBean和@Bean的区别?
        // A: @Bean适合简单对象的声明式配置;FactoryBean适合复杂对象的编程式创建
        
        // Q: 什么时候需要自定义FactoryBean?
        // A: 当对象的创建需要复杂初始化、需要读取外部资源、需要动态决定实现类时
    }
}

/**
 * 总结:BeanFactory vs FactoryBean
 * 
 * BeanFactory = 容器(管理者)
 * - 负责整个IOC容器的运作
 * - 管理所有Bean的生命周期
 * - 处理依赖注入
 * 
 * FactoryBean = 特殊工人(生产者)
 * - 本身是一个Bean,也受容器管理
 * - 负责生产其他复杂的Bean
 * - 封装复杂的创建逻辑
 * 
 * 协作关系:
 * BeanFactory管理FactoryBean,FactoryBean为BeanFactory生产其他Bean
 */

// 面试金句
// "BeanFactory和FactoryBean的区别就像'超市'和'食品加工厂':
//  BeanFactory是'超市',负责管理所有'商品'(Bean),
//  提供上架(注册)、存储(缓存)、售卖(获取)等功能;
//  FactoryBean是'食品加工厂',本身也是'超市'里的一个'特殊商家',
//  但它能生产出其他'商品'(复杂Bean),比如'熟食区'的烤鸭,
//  制作过程很复杂,但顾客只需要在'超市'(BeanFactory)里直接拿到成品。
//  在MyBatis集成中,SqlSessionFactoryBean就是个典型的'食品加工厂',
//  它读取配置文件、解析XML、创建Configuration,最终生产出SqlSessionFactory,
//  而我们使用时只需要@Autowired注入即可完全感知不到背后的复杂性"

如何自定义一个 Starter?

1. Starter核心原理

一句话原理:Spring Boot Starter基于自动配置(Auto-Configuration)机制,通过SPI(spring.factories)加载配置类,结合条件注解(@Conditional)实现按需自动装配,将相关依赖和配置封装为可复用的模块。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
 * Starter的核心机制:自动配置 + 条件装配
 */
// 1. 自动配置类
@Configuration
@ConditionalOnClass(RedisClient.class)  // 当RedisClient存在时才生效
@EnableConfigurationProperties(RedisProperties.class)  // 启用配置属性
public class RedisAutoConfiguration {
    
    @Bean
    @ConditionalOnMissingBean  // 用户没有自定义时才创建
    public RedisClient redisClient(RedisProperties properties) {
        return new RedisClient(properties.getHost(), properties.getPort());
    }
}

// 2. 配置属性类
@ConfigurationProperties(prefix = "custom.redis")
public class RedisProperties {
    private String host = "localhost";  // 默认值
    private int port = 6379;
    // getters/setters
}

// 3. SPI配置文件:META-INF/spring.factories
// org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
// com.example.starter.RedisAutoConfiguration

项目场景:在微服务架构中,多个服务都需要使用Redis、MQ、监控等通用组件。通过自定义Starter封装这些组件的配置和初始化逻辑,新服务只需引入依赖,在application.yml中配置几行参数即可使用,实现了"一次封装,到处使用"的复用效果。

2. 自定义Starter完整实现

一句话原理:自定义Starter需要包含自动配置类(定义Bean创建逻辑)、配置属性类(映射yml配置)、spring.factories(声明自动配置),并遵循命名规范xxx-spring-boot-starter,确保Spring Boot能自动加载。

一句话源码

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/**
 * 完整示例:自定义一个短信发送Starter
 */
// 项目结构
// sms-spring-boot-starter/
// ├── pom.xml
// └── src/main/java/com/example/sms/
//     ├── SmsAutoConfiguration.java
//     ├── SmsProperties.java
//     ├── SmsTemplate.java
//     └── resources/META-INF/spring.factories

// 1. 配置属性类 - 映射application.yml中的配置
@ConfigurationProperties(prefix = "sms")
public class SmsProperties {
    /**
     * 短信服务商类型:aliyun/tencent/huawei
     */
    private String provider = "aliyun";
    
    /**
     * 接入密钥ID
     */
    private String accessKeyId;
    
    /**
     * 接入密钥密码
     */
    private String accessSecret;
    
    /**
     * 短信签名
     */
    private String signName;
    
    // getters/setters 省略
}

// 2. 服务类 - 提供给业务方使用的API
public class SmsTemplate {
    private final SmsProperties properties;
    private final SmsClient client;  // 具体短信客户端
    
    public SmsTemplate(SmsProperties properties) {
        this.properties = properties;
        this.client = createClient(properties);
    }
    
    private SmsClient createClient(SmsProperties properties) {
        switch (properties.getProvider()) {
            case "aliyun":
                return new AliyunSmsClient(properties);
            case "tencent":
                return new TencentSmsClient(properties);
            default:
                throw new IllegalArgumentException("不支持的短信服务商");
        }
    }
    
    /**
     * 发送短信
     * @param phone 手机号
     * @param templateCode 模板代码
     * @param params 模板参数
     */
    public void send(String phone, String templateCode, Map<String, String> params) {
        client.send(phone, templateCode, params);
    }
}

// 3. 自动配置类 - 核心装配逻辑
@Configuration
@EnableConfigurationProperties(SmsProperties.class)  // 启用配置属性
@ConditionalOnClass(SmsTemplate.class)  // 当SmsTemplate存在时才加载
@ConditionalOnProperty(prefix = "sms", name = "enabled", havingValue = "true", matchIfMissing = true)
public class SmsAutoConfiguration {
    
    @Bean
    @ConditionalOnMissingBean  // 用户未自定义时才创建
    public SmsTemplate smsTemplate(SmsProperties properties) {
        return new SmsTemplate(properties);
    }
    
    // 可选:根据不同条件创建不同实现
    @Bean
    @ConditionalOnProperty(prefix = "sms", name = "provider", havingValue = "aliyun")
    public AliyunSmsClient aliyunSmsClient(SmsProperties properties) {
        return new AliyunSmsClient(properties);
    }
}

// 4. SPI配置文件:src/main/resources/META-INF/spring.factories
// org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
// com.example.sms.SmsAutoConfiguration

// 5. 可选:添加spring-configuration-metadata.json提供IDE提示
// src/main/resources/META-INF/spring-configuration-metadata.json
{
  "groups": [
    {
      "name": "sms",
      "type": "com.example.sms.SmsProperties",
      "sourceType": "com.example.sms.SmsProperties"
    }
  ],
  "properties": [
    {
      "name": "sms.provider",
      "type": "java.lang.String",
      "description": "短信服务商类型:aliyun/tencent/huawei",
      "defaultValue": "aliyun"
    },
    {
      "name": "sms.access-key-id",
      "type": "java.lang.String",
      "description": "接入密钥ID"
    }
  ]
}

项目场景:在公司的技术中台建设中,将短信、邮件、OSS等通用能力封装为Starter。业务团队引入依赖后,只需在配置文件中填写accessKey等少量配置,即可获得完整的客户端API,统一了技术栈,降低了使用门槛。

3. 条件注解的最佳实践

一句话原理:Starter的健壮性依赖于条件注解的合理使用,通过@ConditionalOnClass控制依赖存在性,@ConditionalOnProperty控制开关,@ConditionalOnMissingBean允许用户覆盖,实现"开箱即用、按需配置、灵活扩展"。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/**
 * 条件注解的最佳实践
 */
@Configuration
@ConditionalOnClass(RedisOperations.class)  // 只有存在Redis依赖时才加载
@EnableConfigurationProperties(RedisMonitorProperties.class)
public class RedisMonitorAutoConfiguration {
    
    // 1. 通过开关控制是否启用
    @Bean
    @ConditionalOnProperty(prefix = "monitor.redis", name = "enabled", havingValue = "true", matchIfMissing = false)
    public RedisHealthChecker redisHealthChecker(RedisTemplate redisTemplate) {
        return new RedisHealthChecker(redisTemplate);
    }
    
    // 2. 允许用户覆盖默认实现
    @Bean
    @ConditionalOnMissingBean
    public RedisMetricsCollector redisMetricsCollector() {
        return new DefaultRedisMetricsCollector();
    }
    
    // 3. 根据配置创建不同实现
    @Bean
    @ConditionalOnProperty(prefix = "monitor.redis", name = "mode", havingValue = "cluster")
    public RedisClusterMonitor redisClusterMonitor(RedisClusterConnection clusterConnection) {
        return new RedisClusterMonitor(clusterConnection);
    }
    
    // 4. 特定资源存在时才创建
    @Bean
    @ConditionalOnResource(resources = "classpath:redis-monitor-default.yml")
    public RedisMonitorConfigLoader configLoader() {
        return new RedisMonitorConfigLoader("classpath:redis-monitor-default.yml");
    }
    
    // 5. 根据表达式判断
    @Bean
    @ConditionalOnExpression("${monitor.redis.enabled:true} && '${spring.profiles.active}'.contains('prod')")
    public RedisProdOnlyMonitor prodMonitor() {
        return new RedisProdOnlyMonitor();
    }
}

/**
 * 常用条件注解速查
 * 
 * 注解                         作用
 * @ConditionalOnClass          类路径存在指定类时生效
 * @ConditionalOnMissingClass   类路径不存在指定类时生效
 * @ConditionalOnBean           容器中存在指定Bean时生效
 * @ConditionalOnMissingBean    容器中不存在指定Bean时生效
 * @ConditionalOnProperty       配置属性满足条件时生效
 * @ConditionalOnResource       指定资源存在时生效
 * @ConditionalOnWebApplication 当前是Web环境时生效
 * @ConditionalOnNotWebApplication 当前不是Web环境时生效
 * @ConditionalOnExpression     SpEL表达式结果为true时生效
 * @ConditionalOnJava           Java版本满足条件时生效
 */

项目场景:在公司统一监控Starter中,通过@ConditionalOnProperty让用户可以灵活开关各类监控(如Redis监控、数据库监控、JVM监控);通过@ConditionalOnMissingBean允许业务团队自定义监控实现;通过@ConditionalOnExpression实现在生产环境自动启用详细监控,测试环境只启用基础监控。

完整实战指南

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/**
 * 自定义Starter完整实战
 */
public class StarterDevelopmentGuide {
    
    /**
     * 1. 命名规范
     */
    public class NamingConvention {
        // Spring官方Starter: spring-boot-starter-{name}
        // 例如: spring-boot-starter-web, spring-boot-starter-data-redis
        
        // 自定义Starter: {name}-spring-boot-starter
        // 例如: sms-spring-boot-starter, oss-spring-boot-starter
        
        // 非官方但遵循规范,便于识别
    }
    
    /**
     * 2. 依赖管理(pom.xml)
     */
    // <project>
    //     <groupId>com.example</groupId>
    //     <artifactId>sms-spring-boot-starter</artifactId>
    //     <version>1.0.0</version>
    //     
    //     <dependencies>
    //         <!-- Spring Boot自动配置核心 -->
    //         <dependency>
    //             <groupId>org.springframework.boot</groupId>
    //             <artifactId>spring-boot-autoconfigure</artifactId>
    //             <optional>true</optional>
    //         </dependency>
    //         
    //         <!-- 配置属性处理器(生成元数据) -->
    //         <dependency>
    //             <groupId>org.springframework.boot</groupId>
    //             <artifactId>spring-boot-configuration-processor</artifactId>
    //             <optional>true</optional>
    //         </dependency>
    //         
    //         <!-- 实际功能依赖(可选) -->
    //         <dependency>
    //             <groupId>com.aliyun</groupId>
    //             <artifactId>aliyun-java-sdk-core</artifactId>
    //             <optional>true</optional>  <!-- 可选,由使用者决定 -->
    //         </dependency>
    //     </dependencies>
    // </project>
    
    /**
     * 3. 完整示例:多环境配置Starter
     */
    public class MultiEnvStarter {
        
        // 配置属性
        @ConfigurationProperties(prefix = "app.env")
        public class EnvProperties {
            private String current = "dev";
            private List<String> features = new ArrayList<>();
            // getters/setters
        }
        
        // 自动配置
        @Configuration
        @ConditionalOnClass(EnvManager.class)
        @EnableConfigurationProperties(EnvProperties.class)
        public class EnvAutoConfiguration {
            
            @Bean
            @ConditionalOnMissingBean
            public EnvManager envManager(EnvProperties properties) {
                return new EnvManager(properties);
            }
            
            // 根据环境创建不同Bean
            @Bean
            @ConditionalOnProperty(prefix = "app.env", name = "current", havingValue = "dev")
            public DevConfig devConfig() {
                return new DevConfig();
            }
            
            @Bean
            @ConditionalOnProperty(prefix = "app.env", name = "current", havingValue = "prod")
            public ProdConfig prodConfig() {
                return new ProdConfig();
            }
        }
    }
    
    /**
     * 4. 测试Starter
     */
    @SpringBootTest
    @TestPropertySource(properties = {
        "sms.provider=aliyun",
        "sms.access-key-id=test-key",
        "sms.access-secret=test-secret"
    })
    public class StarterTest {
        
        @Autowired
        private SmsTemplate smsTemplate;  // 自动注入
        
        @Test
        public void testSend() {
            smsTemplate.send("13800138000", "SMS_123", Collections.singletonMap("code", "123456"));
            // 验证发送成功
        }
        
        @Test
        public void testProperties() {
            // 验证配置正确加载
        }
    }
    
    /**
     * 5. 发布和使用
     */
    public class Usage {
        
        // 1. 发布到Maven仓库
        // mvn clean install  (本地)
        // mvn deploy         (远程)
        
        // 2. 业务项目引入
        // <dependency>
        //     <groupId>com.example</groupId>
        //     <artifactId>sms-spring-boot-starter</artifactId>
        //     <version>1.0.0</version>
        // </dependency>
        
        // 3. 配置application.yml
        // sms:
        //   provider: aliyun
        //   access-key-id: LTAI4G123456
        //   access-secret: 123456abcdef
        //   sign-name: 我的公司
        
        // 4. 在代码中使用
        @Service
        public class UserService {
            @Autowired
            private SmsTemplate smsTemplate;  // 直接注入使用
            
            public void sendVerificationCode(String phone, String code) {
                smsTemplate.send(phone, "VERIFY_CODE", Map.of("code", code));
            }
        }
    }
    
    /**
     * 6. 最佳实践总结
     */
    public class BestPractices {
        // 1. 合理的默认值:让Starter开箱即用
        // 2. 充分的配置项:覆盖所有可调参数
        // 3. 条件注解:避免不必要的Bean创建
        // 4. 元数据生成:提供IDE提示
        // 5. 单元测试:覆盖各种配置场景
        // 6. 文档说明:README.md写清楚使用方式
        // 7. 版本管理:遵循语义化版本
    }
}

/**
 * 自定义Starter总结表
 * 
 * 组件                  作用
 * 自动配置类            定义Bean的创建逻辑
 * 配置属性类            映射application.yml配置
 * spring.factories     声明自动配置类
 * 条件注解             按需装配,灵活控制
 * 配置元数据            IDE配置提示
 * 
 * 开发步骤:
 * 1. 确定功能范围
 * 2. 设计配置属性
 * 3. 实现自动配置
 * 4. 添加条件控制
 * 5. 提供测试用例
 * 6. 编写使用文档
 */

// 面试金句
// "自定义Starter就像'开一家连锁店':
//  自动配置类是'开店手册'(告诉你怎么开店),
//  配置属性类是'加盟合同'(填写你的具体信息),
//  spring.factories是'工商注册'(让总店知道有新店了),
//  条件注解是'选址策略'(在人多的地方开店,人少的地方不开)。
//  在公司中台建设中,我们把短信、邮件、OSS都做成了Starter,
//  业务团队只需在'加盟合同'(yml)里填上密钥,就能直接使用这些能力,
//  实现了技术能力的'连锁经营',大大提升了开发效率"

Spring 是如何解决循环依赖的?三级缓存的作用是什么?

1. 循环依赖问题本质

一句话原理:循环依赖是指多个Bean之间形成相互引用闭环(如A依赖B,B依赖A),Spring通过三级缓存提前暴露早期引用,结合提前暴露动态代理机制,在Bean未完全初始化完成时就注入依赖,打破循环死锁。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
 * 循环依赖问题演示
 */
@Component
public class ServiceA {
    @Autowired
    private ServiceB serviceB;  // A依赖B
    
    public void doSomething() {
        serviceB.doSomething();
    }
}

@Component
public class ServiceB {
    @Autowired
    private ServiceA serviceA;  // B依赖A,形成循环
    
    public void doSomething() {
        serviceA.doSomething();
    }
}

// 如果Spring没有循环依赖解决机制,会陷入死循环:
// 创建A  需要B  创建B  需要A  创建A  需要B ... 无限循环

项目场景:在复杂的业务系统中,循环依赖经常出现,如订单服务依赖用户服务获取用户信息,用户服务又依赖订单服务查询用户订单,形成双向调用。Spring的三级缓存机制让这种设计成为可能,避免了手动拆解的麻烦。

2. 三级缓存详解

一句话原理:Spring使用三级缓存逐步暴露Bean:一级缓存(singletonObjects)存放完整Bean,二级缓存(earlySingletonObjects)存放提前暴露的早期Bean,三级缓存(singletonFactories)存放Bean工厂,通过层层递进解决不同阶段的依赖问题。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
 * Spring中的三级缓存定义
 */
public class DefaultSingletonBeanRegistry {
    
    // 1. 一级缓存:单例对象缓存(完成品)
    // key: bean名称, value: 完全初始化好的Bean
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    
    // 2. 二级缓存:早期单例对象缓存(半成品)
    // key: bean名称, value: 提前暴露的Bean(尚未完成属性填充和初始化)
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
    
    // 3. 三级缓存:单例工厂缓存
    // key: bean名称, value: 创建Bean的ObjectFactory
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
    
    // 获取Bean的核心方法
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        // 1. 从一级缓存获取(已完成Bean)
        Object singletonObject = this.singletonObjects.get(beanName);
        
        // 2. 如果一级缓存没有,且Bean正在创建中
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                // 3. 从二级缓存获取(早期Bean)
                singletonObject = this.earlySingletonObjects.get(beanName);
                
                // 4. 如果二级缓存也没有,且允许早期引用
                if (singletonObject == null && allowEarlyReference) {
                    // 5. 从三级缓存获取ObjectFactory
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        // 6. 通过工厂创建早期引用(可能包含AOP代理)
                        singletonObject = singletonFactory.getObject();
                        // 7. 放入二级缓存,移除三级缓存
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }
}

项目场景:在配置中心服务中,ConfigService依赖DataSource,DataSource又依赖ConfigService获取配置(动态数据源场景)。三级缓存让ConfigService先暴露早期引用,DataSource注入这个早期引用完成初始化,最后ConfigService再完成完整初始化,完美解决循环依赖。

3. 循环依赖解决完整流程

一句话原理:Spring通过三级缓存的协作完成循环依赖解决:创建A时将其工厂放入三级缓存,发现需要B,转而创建B;B创建时从三级缓存获取A的早期引用注入自己,完成B的创建;B创建后,A再完成后续属性填充和初始化,整个过程环环相扣。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/**
 * 循环依赖解决完整流程源码分析
 */
public class CircularDependencySolution {
    
    // 假设创建顺序:A → B → A(循环)
    
    // 步骤1:开始创建A
    // AbstractAutowireCapableBeanFactory.doCreateBean()
    protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) {
        // 1.1 实例化A(调用构造器,分配内存,但未填充属性)
        BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
        Object bean = instanceWrapper.getWrappedInstance();
        
        // 1.2 将A的工厂放入三级缓存(提前暴露)
        // 这里传入的getEarlyBeanReference可以处理AOP代理
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        
        // 1.3 开始填充属性(发现需要B)
        populateBean(beanName, mbd, instanceWrapper);
        
        // 1.4 初始化A(这一步在B创建完成后才继续)
        exposedObject = initializeBean(beanName, exposedObject, mbd);
        
        return exposedObject;
    }
    
    // 步骤2:填充属性时发现需要B,开始创建B
    // populateBean() → 依赖解析 → getBean("B")
    
    // 步骤3:创建B(重复A的流程)
    // 3.1 实例化B
    // 3.2 将B的工厂放入三级缓存
    // 3.3 填充B的属性(发现需要A)
    
    // 步骤4:B从三级缓存获取A的早期引用
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        // 从三级缓存获取A的工厂,创建早期引用
        // 这个早期引用可能被AOP代理增强
        Object earlyA = singletonFactory.getObject();
        // 放入二级缓存
        earlySingletonObjects.put(beanName, earlyA);
        // B成功注入早期A
    }
    
    // 步骤5:B完成初始化,放入一级缓存
    // 步骤6:A继续执行,从一级缓存获取完整的B注入
    // 步骤7:A完成初始化,放入一级缓存
    
    // 关键方法:提前暴露工厂
    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        synchronized (this.singletonObjects) {
            if (!this.singletonObjects.containsKey(beanName)) {
                // 放入三级缓存
                this.singletonFactories.put(beanName, singletonFactory);
                // 从二级缓存移除(确保干净)
                this.earlySingletonObjects.remove(beanName);
            }
        }
    }
}

项目场景:在电商系统的库存服务和订单服务相互调用时,通过三级缓存机制,两个服务都能正确注入对方。库存服务在创建过程中提前暴露自己,订单服务注入库存服务的早期引用完成初始化,最终两者都完整创建,业务逻辑正常执行。

4. 三级缓存的作用与意义

一句话原理一级缓存存成品,二级缓存存半成品(提前暴露),三级缓存存工厂(支持AOP代理),这种分层设计既保证了Bean的完整生命周期,又通过工厂隔离实现了代理对象的正确创建,是Spring设计的精妙之处。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
 * 三级缓存各司其职
 */
public class ThreeLevelCacheRoles {
    
    // 1. 一级缓存(singletonObjects):最终成品
    // 作用:存放完全初始化好的Bean,供外部直接使用
    // 特点:Bean已填充属性、完成初始化、可能经过AOP增强
    
    // 2. 二级缓存(earlySingletonObjects):早期半成品
    // 作用:存放提前暴露的Bean,解决循环依赖
    // 特点:属性未填充,但已实例化,可能已AOP代理
    
    // 3. 三级缓存(singletonFactories):对象工厂
    // 作用:存放创建Bean的工厂,延迟创建早期引用
    // 特点:支持AOP代理的延迟创建,避免重复代理
    
    // 为什么需要三级缓存,而不是两级?
    public class WhyThreeLevel {
        // 场景:A需要被AOP代理,B依赖A
        
        // 如果只有两级缓存(没有工厂):
        // - 在实例化A后,直接创建A的代理对象放入二级缓存
        // - 问题是:如果A没有被循环依赖,这个代理对象就白创建了
        
        // 三级缓存方案:
        // - 实例化A后,放入工厂(能创建普通对象或代理对象)
        // - 只有当真正发生循环依赖时,才调用工厂创建早期引用
        // - 如果没有循环依赖,工厂不会被调用,避免提前代理
        
        // 工厂方法:处理AOP代理
        protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
            Object exposedObject = bean;
            // 如果有AOP增强器,在这里创建代理对象
            if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
                for (BeanPostProcessor bp : getBeanPostProcessors()) {
                    if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                        SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                        exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                    }
                }
            }
            return exposedObject;  // 可能是原始对象,也可能是代理对象
        }
    }
    
    // 总结:三级缓存设计
    // - 一级缓存:大家都好了
    // - 二级缓存:有人需要我,我先出来帮忙
    // - 三级缓存:我还没准备好,但这是我的名片(工厂),需要时找我
}

项目场景:在需要事务管理的服务中,服务A需要被@Transactional增强(生成代理对象),同时依赖服务B。三级缓存确保在A实例化后只放入工厂,当B需要A时才通过工厂创建代理对象,避免了重复代理,保证了事务功能的正确性。

完整实战指南

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/**
 * 循环依赖实战指南
 */
public class CircularDependencyGuide {
    
    /**
     * 1. 支持循环依赖的场景
     */
    public class SupportedScenarios {
        // ✅ Setter注入(字段注入)支持循环依赖
        @Component
        public class X {
            @Autowired
            private Y y;  // Setter/字段注入
        }
        
        @Component
        public class Y {
            @Autowired
            private X x;  // Setter/字段注入
        }
        
        // ✅ 多例(prototype)不支持循环依赖
        @Scope("prototype")
        @Component
        public class P1 {
            @Autowired
            private P2 p2;  // ❌ 会报错:prototype循环依赖无法解决
        }
    }
    
    /**
     * 2. 不支持循环依赖的场景
     */
    public class UnsupportedScenarios {
        // ❌ 构造器注入
        @Component
        public class A {
            public A(B b) { }  // 构造器注入:循环依赖会报错
        }
        
        // ❌ 单例Bean的构造器注入 + 循环依赖
        // 报错:Requested bean is currently in creation: Is there an unresolvable circular reference?
        
        // ❌ 非单例作用域
        @Scope("prototype")
        @Component
        public class M {
            @Autowired
            private N n;  // prototype + singleton 可以,但prototype之间不行
        }
    }
    
    /**
     * 3. 源码调试技巧
     */
    public class DebugTechniques {
        
        public void debugCircularDependency() {
            // 断点1: AbstractBeanFactory.doGetBean()
            // 观察Bean创建过程
            
            // 断点2: DefaultSingletonBeanRegistry.getSingleton()
            // 观察三级缓存的变化
            
            // 断点3: AbstractAutowireCapableBeanFactory.populateBean()
            // 观察属性填充时的依赖解析
            
            // 断点4: 循环依赖发生时,观察三级缓存->二级缓存->一级缓存的转移
        }
        
        // 开启循环依赖日志
        // application.properties
        // debug=true
        // logging.level.org.springframework.beans.factory.support=DEBUG
        
        // 日志示例:
        // Creating shared instance of singleton bean 'serviceA'
        // Creating shared instance of singleton bean 'serviceB'
        // Eagerly caching bean 'serviceA' to allow for resolving potential circular references
    }
    
    /**
     * 4. 解决循环依赖的设计方案
     */
    public class DesignSolutions {
        
        // 方案1:重构代码,消除循环
        // 使用中介者模式,将相互依赖的逻辑抽离到第三方
        @Component
        public class OrderService {
            // 只依赖中介者
            private final OrderUserMediator mediator;
        }
        
        @Component
        public class UserService {
            // 只依赖中介者
            private final OrderUserMediator mediator;
        }
        
        @Component
        public class OrderUserMediator {
            @Autowired
            private OrderService orderService;
            @Autowired
            private UserService userService;
            // 协调逻辑
        }
        
        // 方案2:使用@Lazy延迟加载
        @Component
        public class LazyA {
            @Autowired
            @Lazy  // 延迟加载,注入代理对象
            private LazyB b;
        }
        
        // 方案3:使用setter注入替代字段注入(更灵活)
        @Component
        public class SetterA {
            private SetterB b;
            
            @Autowired
            public void setB(SetterB b) {
                this.b = b;
            }
        }
    }
    
    /**
     * 5. 面试常见问题
     */
    public class InterviewQA {
        
        // Q: Spring能解决所有循环依赖吗?
        // A: 不能。只支持单例、setter/字段注入的循环依赖,不支持构造器注入和prototype
        
        // Q: 为什么要三级缓存?两级不够吗?
        // A: 三级缓存支持AOP代理的延迟创建,避免无循环依赖时的提前代理
        
        // Q: 如何检测循环依赖?
        // A: 通过isSingletonCurrentlyInCreation()判断Bean是否正在创建中
        
        // Q: 开启循环依赖有性能影响吗?
        // A: 有轻微影响(缓存查找、工厂调用),但通常可忽略
    }
}

/**
 * 三级缓存总结表
 * 
 * 缓存级别       名称                    作用                    存储内容
 * 一级           singletonObjects        存放完整Bean            已完成Bean
 * 二级           earlySingletonObjects   存放早期Bean            未完成但已暴露的Bean
 * 三级           singletonFactories      存放Bean工厂            可以创建Bean的工厂
 * 
 * 解决流程:
 * A创建 → 放入三级缓存 → 发现依赖B → B创建 → 从三级缓存获取A工厂 → 
 * 创建A早期引用 → 放入二级缓存 → B完成创建 → A从一级缓存获取B → 
 * A完成创建 → 放入一级缓存
 */

// 面试金句
// "Spring的三级缓存解决循环依赖就像'相亲'的过程:
//  一级缓存是'已婚'(完全稳定),
//  二级缓存是'订婚'(虽然没领证,但已经定下来了),
//  三级缓存是'有介绍人'(可以通过介绍人随时联系)。
//  当A和B互相看中时,A先通过介绍人(三级缓存)放出消息,
//  B看到后赶紧定下A(二级缓存),然后B自己稳定下来(一级缓存),
//  最后A也稳定下来,两人修成正果。
//  如果没有介绍人(三级缓存),A一上来就求婚(创建代理),
//  万一B没看上A(没有循环依赖),这婚就白求了(代理白创建)。
//  理解这个机制,才能明白为什么构造器注入不支持循环依赖,
//  因为构造器注入就像'必须领证才能见面',根本没法提前接触"

如果构造器注入出现循环依赖,Spring 能解决吗?为什么?

1. 构造器注入的本质问题

一句话原理:构造器注入的循环依赖无法解决,因为构造器在Bean实例化阶段就必须传入依赖对象,而此时Bean尚未创建,无法通过三级缓存获取早期引用,形成"先有鸡还是先有蛋"的死锁。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
 * 构造器注入循环依赖演示
 */
@Component
public class ServiceA {
    private final ServiceB serviceB;  // final强制构造器注入
    
    // 构造器注入:创建A时必须传入B
    public ServiceA(ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}

@Component
public class ServiceB {
    private final ServiceA serviceA;  // final强制构造器注入
    
    // 构造器注入:创建B时必须传入A
    public ServiceB(ServiceA serviceA) {
        this.serviceA = serviceA;
    }
}

// 启动报错:
// ┌─────┐
// |  serviceA defined in file [ServiceA.class]
// ↑     ↓
// |  serviceB defined in file [ServiceB.class]
// └─────┘
// 
// Error: Requested bean is currently in creation: Is there an unresolvable circular reference?
// 无法解决的循环引用

项目场景:在重构遗留系统时,团队坚持使用构造器注入保证依赖不可变,但遇到了两个核心服务相互调用的情况。启动时直接报错,不得不通过引入中介者模式重构代码,将相互依赖的接口抽离到第三个服务中,才解决了问题。

2. 为什么构造器注入无法解决

一句话原理:构造器注入发生在Bean实例化阶段,此时Spring无法通过三级缓存提前暴露尚未创建的对象;而setter/字段注入发生在属性填充阶段,此时Bean已实例化,可以提前暴露早期引用,这是两者在生命周期上的本质差异。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/**
 * 构造器注入 vs Setter注入 生命周期对比
 */
public class InjectionLifecycleComparison {
    
    // ============ Setter/字段注入流程(可解决循环依赖) ============
    // 1. 实例化A(调用无参构造器,创建原始对象)
    // 2. 将A的工厂放入三级缓存(提前暴露)
    // 3. 填充A的属性(发现需要B)
    // 4. 创建B(B通过三级缓存获取A的早期引用)
    // 5. B完成初始化
    // 6. A注入完整的B
    // 7. A完成初始化
    
    // ============ 构造器注入流程(无法解决循环依赖) ============
    // 1. 创建A,调用有参构造器,但此时需要传入B
    // 2. B还没创建,去创建B
    // 3. 创建B,调用有参构造器,但此时需要传入A
    // 4. A还没创建完成,无法提供
    // 5. 死循环,抛出异常
    
    // Spring源码中的判断逻辑
    public class ConstructorCircularDependency {
        
        // AbstractAutowireCapableBeanFactory.createBeanInstance()
        protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
            // 确定使用哪个构造器
            Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
            if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR) {
                // 使用构造器自动注入
                return autowireConstructor(beanName, mbd, ctors, args);
                // 这里会直接调用构造器,传入依赖的Bean
                // 如果依赖的Bean还没创建,会触发getBean()
                // 但此时当前Bean还没放入三级缓存,无法提前暴露
            }
            // 使用默认构造器
            return instantiateBean(beanName, mbd);
        }
        
        // ConstructorResolver.autowireConstructor()
        // 这里会通过getBean()获取构造器参数
        // 导致循环依赖无法解决
    }
    
    // 核心原因:三级缓存的getSingleton()时机
    public class ThreeLevelCacheTiming {
        
        // Setter注入时,Bean已经实例化,并放入三级缓存
        // 所以B可以拿到A的早期引用
        
        // 构造器注入时,Bean还没实例化,三级缓存中还没有
        // B无法拿到A的任何信息,陷入死锁
    }
}

项目场景:在开发支付网关时,PaymentServiceRefundService通过构造器相互注入。启动时报循环依赖错误,通过日志分析发现两者都在实例化阶段等待对方,形成了典型的"死锁"。最终改为setter注入,让其中一个先实例化并暴露早期引用,问题解决。

3. 解决方案与最佳实践

一句话原理:解决构造器循环依赖有三种方案:重构代码消除循环(推荐)、改用setter/字段注入(快速解决)、使用@Lazy延迟加载(打破闭环),但最根本的是通过分层设计避免双向依赖。

一句话源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/**
 * 构造器循环依赖解决方案
 */
public class ConstructorCircularSolutions {
    
    // ============ 方案1:重构代码(推荐) ============
    // 将相互依赖的接口抽离到第三个服务中
    @Component
    public class OrderService {
        private final OrderUserMediator mediator;
        
        public OrderService(OrderUserMediator mediator) {
            this.mediator = mediator;  // 只依赖中介者
        }
    }
    
    @Component
    public class UserService {
        private final OrderUserMediator mediator;
        
        public UserService(OrderUserMediator mediator) {
            this.mediator = mediator;  // 只依赖中介者
        }
    }
    
    @Component
    public class OrderUserMediator {
        private final OrderService orderService;
        private final UserService userService;
        
        public OrderUserMediator(OrderService orderService, UserService userService) {
            this.orderService = orderService;
            this.userService = userService;  // 中介者同时依赖两者
        }
        
        // 协调方法
        public void processOrderUserRelation() {
            // 通过中介者协调两个服务的交互
        }
    }
    
    // ============ 方案2:改为setter注入(快速解决) ============
    @Component
    public class PaymentService {
        private RefundService refundService;  // 去掉final
        
        // 构造器只注入非循环依赖
        public PaymentService(OtherService other) {
            // 正常的依赖
        }
        
        @Autowired
        public void setRefundService(RefundService refundService) {
            this.refundService = refundService;  // setter注入循环依赖部分
        }
    }
    
    // ============ 方案3:使用@Lazy延迟加载 ============
    @Component
    public class ProductService {
        private final InventoryService inventoryService;
        
        public ProductService(@Lazy InventoryService inventoryService) {
            // @Lazy会注入一个代理对象,真正使用时才创建
            this.inventoryService = inventoryService;
        }
    }
    
    @Component
    public class InventoryService {
        private final ProductService productService;
        
        public InventoryService(ProductService productService) {
            this.productService = productService;
        }
    }
    
    // ============ 方案4:使用ApplicationContext延迟获取 ============
    @Component
    public class ContextAwareService {
        private final ApplicationContext context;
        
        public ContextAwareService(ApplicationContext context) {
            this.context = context;
        }
        
        public void doSomething() {
            // 需要时再从容器获取,而不是在构造时
            OtherService other = context.getBean(OtherService.class);
        }
    }
}

项目场景:在重构订单核心系统时,发现OrderServiceStockService存在双向依赖。团队评估后认为这两个服务应该合并为一个OrderStockService,通过重构彻底消除了循环依赖,代码更清晰,也符合领域驱动设计的原则。

完整实战指南

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/**
 * 构造器循环依赖完整指南
 */
public class ConstructorCircularGuide {
    
    /**
     * 1. 源码调试:查看异常抛出位置
     */
    public class DebugLocation {
        // 断点1: AbstractBeanFactory.doGetBean()
        // 查看Bean创建状态
        
        // 断点2: DefaultSingletonBeanRegistry.getSingleton()
        // 观察循环依赖检测
        
        // 断点3: AbstractAutowireCapableBeanFactory.autowireConstructor()
        // 查看构造器参数解析过程
        
        // 异常抛出位置:
        // DefaultSingletonBeanRegistry.beforeSingletonCreation()
        // 检测到Bean正在创建中又再次请求,抛出异常
    }
    
    /**
     * 2. 检测哪些Bean存在循环依赖
     */
    public class DetectionMethod {
        // 启动时增加参数,打印循环依赖日志
        // -Dspring.profiles.active=dev -Ddebug=true
        
        // 或配置application.properties
        // debug=true
        
        // 日志示例:
        // 2019-10-15 10:30:45.123 DEBUG 12345 --- [main] .s.b.f.s.DefaultListableBeanFactory : Creating shared instance of singleton bean 'serviceA'
        // 2019-10-15 10:30:45.124 DEBUG 12345 --- [main] .s.b.f.s.DefaultListableBeanFactory : Creating shared instance of singleton bean 'serviceB'
        // 2019-10-15 10:30:45.125 ERROR 12345 --- [main] o.s.b.f.s.DefaultSingletonBeanRegistry : Bean 'serviceA' is a circular reference
    }
    
    /**
     * 3. 设计原则:避免循环依赖
     */
    public class DesignPrinciples {
        
        // 原则1:单向依赖
        // Service层依赖DAO层,但DAO层不能依赖Service层
        
        // 原则2:分层清晰
        // Controller → Service → DAO,严禁反向依赖
        
        // 原则3:抽象解耦
        // 通过接口和事件机制解耦双向依赖
        
        // 原则4:优先构造器注入
        // 但发现循环依赖时及时重构
        
        // 原则5:使用中介者模式
        // 将交叉逻辑抽离到独立的协调者
    }
    
    /**
     * 4. 单元测试验证
     */
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class CircularDependencyTest {
        
        @Autowired
        private ApplicationContext context;
        
        @Test(expected = BeanCurrentlyInCreationException.class)
        public void testConstructorCircular() {
            // 构造器循环依赖会抛出异常
            context.getBean(ServiceA.class);
        }
        
        @Test
        public void testSetterCircular() {
            // setter循环依赖可以正常工作
            ServiceX x = context.getBean(ServiceX.class);
            ServiceY y = context.getBean(ServiceY.class);
            
            assertNotNull(x.getY());
            assertNotNull(y.getX());
        }
    }
    
    /**
     * 5. 面试常见问题
     */
    public class InterviewQA {
        
        // Q: Spring官方推荐构造器注入,遇到循环依赖怎么办?
        // A: 重构代码,消除循环依赖。循环依赖本身就是设计问题
        
        // Q: 为什么构造器注入循环依赖报错,而setter不报错?
        // A: 生命周期不同:构造器在实例化阶段,setter在属性填充阶段
        
        // Q: @Lazy能解决所有构造器循环依赖吗?
        // A: 能解决,但会引入代理对象,且延迟初始化可能带来空指针风险
        
        // Q: 如何避免构造器循环依赖?
        // A: 遵循分层设计,单向依赖,必要时引入中介者
    }
}

/**
 * 总结对比表
 * 
 * 注入方式      能否解决循环依赖      原因                         推荐度
 * 构造器注入    ❌ 不能              实例化阶段,无法提前暴露         ⭐⭐⭐(无循环时)
 * Setter注入    ✅ 能                属性填充阶段,有三级缓存         ⭐⭐(有循环时)
 * 字段注入      ✅ 能                同Setter                       ⭐(测试不便)
 * 
 * 最佳实践:
 * 1. 优先构造器注入(保证不可变,依赖明确)
 * 2. 发现循环依赖时,优先重构代码
 * 3. 临时解决方案:改为setter注入或@Lazy
 * 4. 长期方案:引入中介者或事件驱动
 */

// 面试金句
// "构造器注入的循环依赖就像'双向验血':A要抽B的血,B要抽A的血,
//  但两人都怕疼,必须先抽对方的才能抽自己的,结果永远抽不成。
//  Spring的三级缓存机制就像'献血车',允许你先'登记'(放入三级缓存),
//  然后别人可以先拿你的'献血证'(早期引用)去办事,你后面再补抽血。
//  但构造器注入的问题是:你连'登记'都没做,就直接要求对方先献血,
//  对方也同样要求你,导致'献血车'还没开过来,两人就僵住了。
//  所以官方推荐构造器注入,但要求我们设计好依赖关系,避免这种'双向绑定'。
//  在项目中,我遇到这种情况时,通常会用中介者模式解耦,
//  让两个服务通过'协调员'沟通既保持了构造器注入的优点又避免了循环依赖"