1. Spring容器基础概念与项目搭建
Spring框架作为Java企业级开发的事实标准,其核心机制IoC(控制反转)和DI(依赖注入)都是通过Spring容器实现的。理解容器的启动和关闭过程,是掌握Spring框架的基础。
1.1 为什么需要Spring容器
传统Java开发中,对象创建和依赖管理由开发者手动完成,导致代码耦合度高、难以维护。Spring容器通过以下方式解决这些问题:
- 集中管理所有对象的生命周期
- 自动处理对象间的依赖关系
- 提供统一的配置管理方式
- 支持AOP等高级特性
1.2 创建Maven工程
使用Maven管理项目依赖是现代Java开发的标配。创建过程需要注意几个关键点:
- 在IDE(如IntelliJ IDEA)中选择Maven项目模板
- 填写GroupId和ArtifactId时遵循Maven命名规范
- 确保项目结构符合Maven标准目录布局
提示:建议使用最新稳定版的Maven(当前为3.8.x),避免因版本过旧导致的依赖解析问题。
1.3 添加Spring依赖
在pom.xml中添加依赖时,需要注意版本兼容性。虽然示例中使用的是较旧的4.1.3.RELEASE,但实际开发中建议使用Spring 5.x的最新稳定版:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.18</version> </dependency>为什么选择spring-context而不是spring-webmvc?
- spring-context包含核心容器功能
- 只有需要开发Web应用时才添加spring-webmvc
- 避免引入不必要的依赖
1.4 验证依赖加载
执行mvn clean install后,可以通过以下方式验证依赖是否正确加载:
- 检查IDE的依赖视图
- 查看本地Maven仓库(默认在~/.m2/repository)
- 运行
mvn dependency:tree查看依赖树
常见问题:如果依赖下载失败,可能是网络问题或仓库配置错误。可以尝试:
- 检查Maven的settings.xml配置
- 更换为国内镜像源(如阿里云)
- 删除本地仓库中对应依赖的文件夹后重新下载
2. Spring配置与容器初始化
2.1 配置文件详解
applicationContext.xml是Spring的核心配置文件,其基本结构如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- Bean定义 --> </beans>关键点说明:
- xmlns声明XML命名空间
- xsi:schemaLocation指定XML Schema验证文件位置
- 现代Spring项目也可以使用Java配置或注解方式
2.2 Bean定义基础
定义一个简单的Bean:
<bean id="aoo" class="com.example.Aoo"/>属性说明:
- id:Bean的唯一标识符,遵循Java变量命名规则
- class:类的全限定名(包名+类名)
- 其他常用属性:scope、lazy-init、init-method、destroy-method等
2.3 容器启动过程分析
当执行以下代码时:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");Spring容器会执行以下操作:
- 解析XML配置文件,创建BeanDefinition
- 根据BeanDefinition实例化Bean
- 注入依赖(属性赋值)
- 调用初始化方法(如果有)
- 将Bean放入容器管理
注意:默认情况下,Bean是在容器启动时创建的(饿汉式)。对于大型应用,这可能导致启动时间过长。
3. Bean生命周期管理
3.1 生命周期回调方法
Spring提供了多种方式定义生命周期回调:
- XML配置方式:
<bean id="exampleBean" class="com.example.ExampleBean" init-method="init" destroy-method="cleanup"/>- 接口实现方式:
public class ExampleBean implements InitializingBean, DisposableBean { public void afterPropertiesSet() { // 初始化逻辑 } public void destroy() { // 清理逻辑 } }- 注解方式:
public class ExampleBean { @PostConstruct public void init() { // 初始化逻辑 } @PreDestroy public void cleanup() { // 清理逻辑 } }3.2 作用域(Scope)详解
Spring支持多种作用域,最常用的是:
- singleton(默认):每个容器中只有一个实例
- prototype:每次请求都创建新实例
- request:每个HTTP请求创建一个实例(Web应用)
- session:每个HTTP会话创建一个实例(Web应用)
- application:整个Web应用共享一个实例
配置示例:
<bean id="accountService" class="com.example.AccountService" scope="prototype"/>3.3 延迟初始化(Lazy-init)
延迟加载可以优化启动性能:
<bean id="lazyBean" class="com.example.LazyBean" lazy-init="true"/>使用场景:
- 初始化耗时的Bean
- 不常用的功能组件
- 可能不会用到的备选实现
注意事项:
- 延迟加载的Bean会在第一次请求时初始化
- 可能导致运行时的首次请求响应变慢
- 与某些AOP功能配合使用时需要特别注意
4. 容器关闭与资源清理
4.1 正确关闭Spring容器
对于非Web应用,需要手动关闭容器以触发destroy方法:
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); // 应用逻辑... context.close(); // 触发所有单例Bean的destroy方法4.2 关闭时的执行顺序
容器关闭时会:
- 发布ContextClosedEvent事件
- 调用所有实现了DisposableBean接口的Bean的destroy()方法
- 调用所有配置了destroy-method的方法
重要:关闭顺序与初始化顺序相反,遵循"后进先出"原则
4.3 资源清理最佳实践
- 数据库连接池:确保所有连接被正确关闭
- 线程池:优雅关闭,处理未完成任务
- 文件/网络资源:释放文件句柄和网络连接
- 缓存:持久化未保存的数据
示例代码:
public class ResourceHolder { private DataSource dataSource; @PreDestroy public void cleanup() { try { dataSource.getConnection().close(); } catch (SQLException e) { // 记录日志 } } }5. 高级特性与实战技巧
5.1 组合配置与模块化
大型项目应该拆分配置文件:
ApplicationContext context = new ClassPathXmlApplicationContext( "classpath:spring-dao.xml", "classpath:spring-service.xml", "classpath:spring-web.xml" );现代Spring项目更推荐使用@Configuration注解的Java配置类:
@Configuration @Import({DaoConfig.class, ServiceConfig.class}) @ComponentScan("com.example") public class AppConfig { // Bean定义 }5.2 条件化Bean注册
Spring 4.0+支持条件化Bean定义:
@Bean @Conditional(DevEnvCondition.class) public DataSource devDataSource() { // 开发环境数据源 }5.3 容器扩展点
- BeanPostProcessor:在Bean初始化前后插入自定义逻辑
- BeanFactoryPostProcessor:修改BeanDefinition
- FactoryBean:自定义复杂对象的创建逻辑
示例:自定义BeanPostProcessor
public class CustomBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) { // 初始化前处理 return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) { // 初始化后处理 return bean; } }6. 常见问题排查
6.1 Bean创建失败
可能原因:
- 类路径错误:检查class属性值是否正确
- 依赖缺失:确保所有依赖的Bean都已定义
- 构造函数异常:检查构造方法逻辑
- 初始化失败:检查init-method或@PostConstruct方法
排查步骤:
- 查看异常堆栈
- 检查Bean定义
- 验证依赖关系
- 简化测试用例
6.2 循环依赖问题
Spring通过三级缓存解决构造器注入的循环依赖,但仍有局限:
解决方案:
- 改为setter注入
- 使用@Lazy延迟加载
- 重构设计,消除循环依赖
6.3 性能优化建议
- 合理使用延迟加载
- 避免过度使用AOP
- 选择合适的Bean作用域
- 优化自动扫描路径
- 使用@Configuration的proxyBeanMethods=false
7. 现代Spring开发实践
7.1 注解驱动开发
Spring Boot推荐使用注解配置:
@Service public class UserService { @Autowired private UserRepository userRepository; // 业务方法 }核心注解:
- @Component/@Service/@Repository/@Controller
- @Autowired/@Value
- @Configuration/@Bean
- @Profile/@Conditional
7.2 配置最佳实践
- 外部化配置:application.properties/yml
- 环境隔离:@Profile或spring.profiles.active
- 类型安全配置:@ConfigurationProperties
7.3 测试支持
Spring提供强大的测试支持:
@SpringJUnitConfig(classes = AppConfig.class) public class UserServiceTest { @Autowired private UserService userService; @Test void testUserCreation() { // 测试逻辑 } }测试技巧:
- 使用@MockBean模拟依赖
- 利用TestPropertySource覆盖配置
- 使用@DirtiesContext重置应用上下文
在实际项目中,我通常会根据模块功能划分多个@Configuration类,结合条件化配置实现环境隔离。对于核心服务Bean,明确指定其作用域和初始化策略,避免隐式行为导致的意外问题。容器关闭时的资源清理尤为重要,特别是对于持有系统级资源的Bean,必须确保在destroy方法中正确释放。