Spring Java配置:告别XML新时代
以下内容是 Spring Framework 官方文档中关于“基于 Java 的容器配置”(1.12. Java-based Container Configuration)的一节,它系统地介绍了如何使用 Java 代码和注解 来替代传统的 XML 配置文件(如 applicationContext.xml),实现对 Spring IoC 容器的完全编程式配置。
下面我将用清晰、结构化的方式为你逐段解析这段内容的核心思想,并帮助你理解:
🌟 总体目标:用 Java 代替 XML 配置
在早期 Spring 版本中,我们通过编写 beans.xml 文件来定义 Bean:
<beans><bean id="myService" class="com.acme.MyServiceImpl"/>
</beans>
但从 Spring 3.0 开始,官方推荐使用 纯 Java 类 + 注解 的方式来配置 Spring 容器。这种方式更类型安全、易于调试、支持重构,也更适合现代开发。
✅ 目标:实现 零 XML 的 Spring 应用。
🔑 核心概念:@Configuration 和 @Bean
这是整个 Java 配置模式的基石。
| 注解 | 作用 |
|---|---|
@Configuration | 表示这个类是一个“配置类”,相当于一个 XML 配置文件 |
@Bean | 表示这个方法返回的对象应该被 Spring 容器管理(即注册为一个 Bean) |
示例代码
@Configuration
public class AppConfig {@Beanpublic MyService myService() {return new MyServiceImpl();}
}
等价于以下 XML:
<beans><bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>
📌 关键点:
- 方法名
myService()就是 Bean 的名字(ID)。 - 返回值
MyServiceImpl()是实际创建的 Bean 实例。 - Spring 会把这个方法的返回值放入 IoC 容器中进行管理。
⚖️ “全功能” vs “轻量级” 模式(Full vs Lite Mode)
这是一个非常重要的区别!
| 模式 | 条件 | 特性 |
|---|---|---|
| 全功能模式(Full) | 类上有 @Configuration | 支持方法间依赖调用(内部调用会被代理拦截) |
| 轻量模式(Lite) | 类上没有 @Configuration(比如只是 @Component 或普通类) | 不支持跨方法依赖,仅作为工厂方法使用 |
🧩 全功能模式(推荐)
@Configuration
public class AppConfig {@Beanpublic DataSource dataSource() {return new EmbeddedDatabaseBuilder().build();}@Beanpublic JdbcTemplate jdbcTemplate() {return new JdbcTemplate(dataSource()); // 调用另一个 @Bean 方法}
}
✅ 在 jdbcTemplate() 中调用 dataSource(),Spring 会确保返回的是同一个单例 Bean(通过 CGLIB 动态代理实现)。
❌ 如果你在“轻量模式”下这样做:
@Component // 注意:不是 @Configuration
public class LiteConfig {@Beanpublic DataSource dataSource() { ... }@Beanpublic JdbcTemplate jdbcTemplate() {return new JdbcTemplate(dataSource()); // ❌ 新建了一个实例!}
}
⚠️ 这里的 dataSource() 是普通 Java 方法调用,每次都会新建对象,破坏了 Singleton 语义!
📌 所以结论:
✅ 尽量把
@Bean方法写在@Configuration类中,避免意外行为。
🚀 如何启动容器?使用 AnnotationConfigApplicationContext
以前我们用:
new ClassPathXmlApplicationContext("beans.xml");
现在我们可以用:
new AnnotationConfigApplicationContext(AppConfig.class);
这意味着:直接传入 Java 配置类来初始化 Spring 容器。
支持多种输入类型:
@Configuration类@Component类(含@Service,@Repository等)- 使用 JSR-330 注解(如
@Named)的类
✅ 方式一:构造函数传参
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class, UserService.class, OrderService.class
);
✅ 方式二:编程式注册(register)
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AppConfig.class);
ctx.register(OtherConfig.class);
ctx.refresh(); // 必须手动刷新
💡
refresh()触发 Bean 的加载与初始化。
✅ 方式三:启用组件扫描(scan)
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.scan("com.acme"); // 扫描包下的所有 @Component/@Configuration 类
ctx.refresh();
这相当于 XML 中的:
<context:component-scan base-package="com.acme"/>
🔍 启用组件扫描:@ComponentScan
如果你想让 Spring 自动发现并注册带有 @Component、@Service 等注解的类,可以在配置类上加上:
@Configuration
@ComponentScan(basePackages = "com.acme")
public class AppConfig {
}
这样就不需要手动一个个写 @Bean 了。
✅ 推荐做法:主配置类加
@ComponentScan,业务类加@Service/@Repository等。
📌 注意:@Configuration 本身也被 @Component 注解标记,所以它也会被扫描到。
☁️ Web 应用支持:AnnotationConfigWebApplicationContext
对于 Web 项目(如 Spring MVC),你需要一个能运行在 Servlet 容器中的上下文。
Spring 提供了对应的 Web 版本:
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
配置 web.xml 示例
<web-app><!-- 指定根容器使用 Java 配置 --><context-param><param-name>contextClass</param-name><param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value></context-param><!-- 指定配置类 --><context-param><param-name>contextConfigLocation</param-name><param-value>com.acme.AppConfig</param-value></context-param><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><!-- DispatcherServlet 也使用 Java 配置 --><servlet><servlet-name>dispatcher</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextClass</param-name><param-value>...AnnotationConfigWebApplicationContext</param-value></init-param><init-param><param-name>contextConfigLocation</param-name><param-value>com.acme.web.MvcConfig</param-value></init-param></servlet>
</web-app>
📌 这样就可以完全摆脱 spring-servlet.xml 文件!
📦 更高级的主题(后续小节预告)
文档还提到了一些进阶功能,虽然没展开讲,但值得了解:
| 主题 | 说明 |
|---|---|
Bean Definition Profiles (@Profile) | 根据环境激活不同配置(如 dev/test/prod) |
| PropertySource 抽象 | 加载属性文件(.properties 或 .yml) |
| @PropertySource | 将外部配置文件加载进 Spring 环境 |
占位符解析 (${}) | 在 @Value 中使用 ${jdbc.url} 获取配置值 |
这些将在后续章节详细介绍。
✅ 总结:你应该怎么理解?
| 传统 XML 配置 | 新的 Java 配置 |
|---|---|
<beans> 标签 | @Configuration 类 |
<bean> 标签 | @Bean 方法 |
<context:component-scan> | @ComponentScan 注解或 ctx.scan() |
ClassPathXmlApplicationContext | AnnotationConfigApplicationContext |
| Web XML 配置 | AnnotationConfigWebApplicationContext |
🎯 最佳实践建议
- 优先使用
@Configuration + @Bean来做核心组件配置(如数据源、JPA、Redis 等)。 - 使用
@ComponentScan自动扫描业务组件(Service、Repository)。 - 避免在非
@Configuration类中使用@Bean,除非你明确知道自己在做什么(lite 模式风险)。 - Web 项目中使用
AnnotationConfigWebApplicationContext替代 XML。 - 结合
@Profile和@PropertySource实现多环境配置。
🔄 对比记忆表
| 功能 | XML 写法 | Java 注解写法 |
|---|---|---|
| 定义配置文件 | <beans> | @Configuration 类 |
| 定义一个 Bean | <bean class="X"/> | @Bean X x() { return new X(); } |
| 组件扫描 | <context:component-scan base-package="x"/> | @ComponentScan("x") |
| 创建容器 | new ClassPathXmlApplicationContext("x.xml") | new AnnotationConfigApplicationContext(X.class) |
| Web 容器 | XmlWebApplicationContext | AnnotationConfigWebApplicationContext |
💬 结语
你现在看到的是 Spring 从“XML 驱动”转向“Java 驱动”的关键设计。这套机制后来成为 Spring Boot 自动配置的基础。
如果你正在学习 Spring Boot,你会发现它的自动配置类(@Configuration 类)大量使用了这种模式。
需要我帮你画一张思维导图?或者写一个完整的例子(包含多个配置类、组件扫描、属性注入)来演示整个流程吗?
