1. 面试官为什么爱问Bean创建方式?
这个问题在Spring面试中出现频率极高,根本原因在于它能全面考察候选人对Spring IoC容器的理解深度。作为Spring框架最核心的机制,Bean的创建过程涉及:
- 依赖注入的实现原理
- 容器生命周期管理
- 配置元数据的多种形式
- 扩展点的灵活运用
我在技术面试中通常会根据候选人对这个问题的回答,快速判断其是停留在API使用层面,还是真正理解Spring的设计哲学。下面通过实际案例拆解7种创建方式及其适用场景。
2. XML配置方式:最原始的Bean定义
2.1 基础配置示例
xml复制<beans>
<bean id="userService" class="com.example.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" class="com.example.UserDaoImpl"/>
</beans>
这种方式的优势在于:
- 配置与代码完全分离
- 支持运行时动态修改(结合PropertyPlaceholderConfigurer)
- 适合遗留系统维护
实际踩坑:XML中bean的init-method/destroy-method配置若与方法签名不匹配,会导致生命周期回调失效且无编译期报错
2.2 高级特性
xml复制<!-- 继承配置 -->
<bean id="baseService" abstract="true">
<property name="config" value="default"/>
</bean>
<bean id="extendedService" parent="baseService">
<property name="config" value="custom"/>
</bean>
<!-- 工厂方法 -->
<bean id="clientService"
class="com.example.ClientService"
factory-method="createInstance"/>
3. 注解驱动方式:现代Spring应用的标配
3.1 组件扫描机制
java复制@Configuration
@ComponentScan("com.example")
public class AppConfig {
@Bean
public DataSource dataSource() {
return new HikariDataSource();
}
}
关键注解:
- @Component(通用组件)
- @Service(业务层)
- @Repository(持久层)
- @Controller(Web层)
经验:在大型项目中建议明确指定扫描路径(如@ComponentScan(basePackages = "...")),避免意外扫描到第三方jar中的组件
3.2 条件化装配实战
java复制@Bean
@ConditionalOnClass(name = "com.redis.clients.jedis.Jedis")
public RedisTemplate<String, Object> redisTemplate() {
// 当存在Jedis类时才创建Bean
}
常用条件注解:
- @ConditionalOnProperty
- @ConditionalOnMissingBean
- @ConditionalOnWebApplication
4. Java显式配置:精准控制的艺术
4.1 @Configuration类的最佳实践
java复制@Configuration
public class SecurityConfig {
@Bean
@Scope("prototype")
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12);
}
@Bean
public AuthenticationManager authManager(
UserDetailsService userDetailsService) {
var provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
return new ProviderManager(provider);
}
}
4.2 解决循环依赖的技巧
java复制@Configuration
public class CircularConfig {
@Bean
public ServiceA serviceA(ServiceB serviceB) {
return new ServiceA(serviceB);
}
@Bean
public ServiceB serviceB(ServiceC serviceC) {
return new ServiceB(serviceC);
}
@Bean
public ServiceC serviceC(ServiceA serviceA) {
return new ServiceC(serviceA);
}
}
处理方案:
- 使用@Lazy延迟初始化
- 改为setter注入
- 重构设计消除循环
5. 工厂模式创建:复杂对象的诞生
5.1 静态工厂方法
java复制public class ClientFactory {
public static Client create(String type) {
return switch (type) {
case "VIP" -> new VipClient();
case "NORMAL" -> new NormalClient();
default -> throw new IllegalArgumentException();
};
}
}
// 配置类
@Bean
public Client client() {
return ClientFactory.create("VIP");
}
5.2 FactoryBean接口实现
java复制public class MyFactoryBean implements FactoryBean<ComplexObject> {
@Override
public ComplexObject getObject() {
return new ComplexObject(initParams());
}
@Override
public Class<?> getObjectType() {
return ComplexObject.class;
}
private Map<String, String> initParams() {
// 复杂的初始化逻辑
}
}
// 使用时直接注入ComplexObject而非FactoryBean
@Autowired
private ComplexObject complexObject;
6. 动态代理生成:AOP背后的魔法
6.1 JDK动态代理
java复制@Bean
public UserService userService() {
return (UserService) Proxy.newProxyInstance(
getClass().getClassLoader(),
new Class[]{UserService.class},
(proxy, method, args) -> {
System.out.println("Before: " + method.getName());
Object result = method.invoke(target, args);
System.out.println("After: " + method.getName());
return result;
}
);
}
6.2 CGLIB代理
java复制@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ProxyConfig {
@Bean
public MyService myService() {
return new MyService();
}
}
// 当proxyTargetClass=true时,即使没有接口也会创建代理
7. 非常规创建方式
7.1 通过SPI扩展
java复制public class CustomBeanDefinitionRegistry
implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(
BeanDefinitionRegistry registry) {
registry.registerBeanDefinition("customBean",
BeanDefinitionBuilder
.rootBeanDefinition(CustomBean.class)
.setScope(ConfigurableBeanFactory.SCOPE_SINGLETON)
.getBeanDefinition());
}
}
7.2 运行时动态注册
java复制@Autowired
private ApplicationContext context;
public void registerBean(String beanName) {
DefaultListableBeanFactory factory = (DefaultListableBeanFactory)
context.getAutowireCapableBeanFactory();
factory.registerBeanDefinition(beanName,
BeanDefinitionBuilder
.genericBeanDefinition(DynamicBean.class)
.addPropertyValue("config", loadConfig())
.getBeanDefinition());
}
8. 生命周期管理的那些坑
8.1 初始化顺序控制
java复制@Bean(initMethod = "init")
@DependsOn({"configLoader", "connectionPool"})
public ServiceCoordinator coordinator() {
return new ServiceCoordinator();
}
8.2 销毁时的资源释放
java复制@Bean(destroyMethod = "cleanup")
public FileProcessor fileProcessor() {
return new FileProcessor(tempDir);
}
// 必须显式关闭容器才会触发destroyMethod
context.registerShutdownHook();
血泪教训:数据库连接池等资源密集型Bean必须正确配置destroyMethod,否则会导致线程泄漏。HikariCP的默认destroyMethod是"close"
9. 面试加分项:Bean创建过程源码解析
9.1 核心流程图示
(此处描述AbstractApplicationContext.refresh()中的关键步骤)
- prepareRefresh()
- obtainFreshBeanFactory()
- prepareBeanFactory()
- postProcessBeanFactory()
- invokeBeanFactoryPostProcessors()
- registerBeanPostProcessors()
- initMessageSource()
- initApplicationEventMulticaster()
- onRefresh()
- registerListeners()
- finishBeanFactoryInitialization()
- finishRefresh()
9.2 关键扩展点
- BeanFactoryPostProcessor:修改BeanDefinition
- BeanPostProcessor:干预Bean实例化过程
- InstantiationAwareBeanPostProcessor:在实例化前后介入
10. 实际工程中的选择策略
根据项目特点选择合适的方式组合:
- 传统企业应用:XML + 注解混合
- Spring Boot项目:注解为主
- 框架扩展开发:FactoryBean + BeanPostProcessor
- 动态配置系统:运行时Bean注册
我在微服务架构中的实践经验:
- 基础组件用Java显式配置(如RedisTemplate)
- 业务服务用@ComponentScan
- 跨服务共享配置用@Import
- 环境差异配置用@Profile
