⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 小哈学Java 「犬小哈」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

面试官想听的不是”五种”

“有几种方式”只是个开场白。面试官真正想知道的是:你理不理解 BeanDefinition 这个核心概念,能不能根据场景选对方式,以及对 FactoryBean 等扩展点有没有认知。答上来”五种”但解释不清为什么有五种,等于白答。

五种方式,一个归宿:BeanDefinition

方式 适用场景 一句话解释
@Component + @ComponentScan 自己写的类 注解声明,包扫描自动发现
XML <bean> 遗留项目维护 传统 XML 配置
@Configuration + @Bean 第三方库集成 方法返回值注册为 Bean
FactoryBean<T> 极复杂对象构造 封装复杂创建逻辑
BeanDefinitionRegistryPostProcessor 框架级动态扩展 运行时编程式注册

所有方式的终点都一样:向 BeanDefinitionRegistry 注册一个 BeanDefinition 对象——Bean 的”蓝图”,包含类名、作用域、初始化方法、属性值等元数据。容器后续根据蓝图,通过反射创建真正的实例。

逐个拆解,代码说话

1. 注解声明:最常用的方式

@Service
public class UserServiceImpl implements UserService { }

@Configuration
@ComponentScan("com.example.service")
public class AppConfig { }

自己写的业务类,贴个注解就完事。Spring Boot 项目 90% 的 Bean 都是这么来的。

2. XML 声明:遗留产物

<bean id="userService" class="com.example.service.UserServiceImpl"/>

新项目别碰。维护老项目时才会看到。

3. @Bean:第三方库集成的杀手锏

@Configuration
public class AppConfig {

@Bean
public DataSource dataSource() {
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setUrl("jdbc:mysql://localhost:3306/test");
return ds;
}

@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}

你改不了人家源码,没法加 @Component——这才是 @Bean 存在的核心理由。 配置 DataSource、HttpClient、各种第三方 SDK,全靠它。

4. FactoryBean:复杂对象的生产车间

public class MyFactoryBean implements FactoryBean<User> {

@Override
public User getObject() throws Exception {
User user = new User();
user.setName("FactoryBean Created");
return user;
}

@Override
public Class<?> getObjectType() { return User.class; }

@Override
public boolean isSingleton() { return true; }
}

容器里实际存在两个 Bean:&myFactoryBean(FactoryBean 本身)和 myFactoryBean(getObject() 返回值)。MyBatis 的 SqlSessionFactoryBean 就是典型案例——内部要构建大量对象,用普通 @Bean 方法写出来能有一百行。

5. 编程式注册:框架开发者的工具

@Component
public class MyBeanRegistrar implements BeanDefinitionRegistryPostProcessor {

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
RootBeanDefinition bd = new RootBeanDefinition(MyDynamicBean.class);
bd.setScope(BeanDefinition.SCOPE_SINGLETON);
registry.registerBeanDefinition("myDynamicBean", bd);
}
}

日常业务开发几乎不会用。框架级别的深度扩展——自动配置、动态代理注册之类的——才会动这个。

怎么选:看两个维度

决策只需要两个问题:

  1. 你能不能改源码? 能改 → @Component;不能改 → @Bean。
  2. 对象构造复杂度如何? 简单 → @Bean 够了;极复杂(远程调用、动态代理、多步初始化)→ FactoryBean。

XML 别选,编程式注册别碰——除非你在写框架。

三个误区,暴露你的认知层级

  1. 混淆 @Component 和 @Bean——@Component 标在类上,@Bean 标在配置类的方法上。前者是”Spring 你来管这个类”,后者是”我组装好了你拿去管”。
  2. 认为只有两种方式——说明对 Spring IoC 的理解还停在入门阶段。知道五种并能说清楚为什么要有五种,才算过关。
  3. Spring Boot 项目里还在大量写 XML——2026 年了,该醒醒了。
文章目录
  1. 1. 面试官想听的不是”五种”
  2. 2. 五种方式,一个归宿:BeanDefinition
  3. 3. 逐个拆解,代码说话
  4. 4. 怎么选:看两个维度
  5. 5. 三个误区,暴露你的认知层级