1. Spring框架的定位与核心价值
Spring框架自2003年诞生以来,已经成为Java企业级开发的事实标准。它最初是为了解决EJB(Enterprise Java Beans)的复杂性而设计,如今已经发展成为一个全方位的应用开发框架。Spring的核心价值主要体现在三个方面:
首先,它通过依赖注入(DI)和面向切面编程(AOP)实现了松耦合的组件管理。这种设计让开发者能够专注于业务逻辑的实现,而不必过多关注对象之间的依赖关系。比如,在传统Java开发中,一个Service类如果需要使用DAO类,通常需要在Service内部直接new一个DAO实例。而在Spring中,我们只需要通过@Autowired注解声明依赖关系,具体的实例化工作由Spring容器完成。
其次,Spring提供了对各种企业级技术的简化集成。无论是JDBC、JPA、JMS这样的Java标准技术,还是Hibernate、MyBatis这样的第三方框架,Spring都提供了统一的模板类和异常体系,大大降低了使用门槛。例如,使用Spring的JdbcTemplate,开发者不再需要手动处理Connection的获取和释放,也不需要考虑SQLException的捕获和处理。
最后,Spring的模块化设计让开发者可以按需选择。核心容器(Core Container)提供了IoC和DI的基础功能,数据访问/集成(Data Access/Integration)模块简化了数据库操作,Web模块支持MVC开发,AOP模块实现了横切关注点的分离。这种设计既保证了框架的灵活性,又避免了"过度设计"的问题。
提示:Spring 5.x版本开始全面支持响应式编程模型,引入了WebFlux框架,为构建高性能、非阻塞的应用提供了新的选择。但在大多数传统企业应用中,基于Servlet的Spring MVC仍然是主流选择。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. IoC容器与Bean生命周期深度解析
2.1 IoC容器的实现机制
Spring IoC(控制反转)容器是整个框架的核心,它负责管理应用中所有对象的生命周期和依赖关系。Spring提供了两种容器实现:BeanFactory和ApplicationContext。前者是最基础的IoC容器,后者是前者的超集,提供了更多企业级功能。
ApplicationContext的主要实现类包括:
- ClassPathXmlApplicationContext:从类路径下的XML配置文件加载上下文
- FileSystemXmlApplicationContext:从文件系统路径加载XML配置
- AnnotationConfigApplicationContext:基于Java配置类加载上下文
现代Spring应用通常采用基于注解的配置方式。@Configuration注解标记一个类为配置类,@Bean注解用于声明一个由Spring管理的bean。例如:
java复制@Configuration
public class AppConfig {
@Bean
public DataSource dataSource() {
return new DriverManagerDataSource("jdbc:mysql://localhost:3306/mydb", "user", "password");
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
2.2 Bean的生命周期详解
Spring管理的Bean有着完整的生命周期,理解这个过程对于解决各种依赖注入问题至关重要。一个Bean的完整生命周期包括以下阶段:
- 实例化:容器调用构造方法创建Bean实例
- 属性填充:通过setter方法或字段注入设置依赖
- Aware接口回调:如果Bean实现了各种Aware接口(如BeanNameAware、ApplicationContextAware),容器会调用相应方法
- 前置初始化:@PostConstruct注解的方法被执行
- 初始化:如果实现了InitializingBean接口,afterPropertiesSet()方法被调用
- 后置初始化:BeanPostProcessor的postProcessAfterInitialization方法
- 使用期:Bean处于就绪状态,可以被应用使用
- 销毁前:@PreDestroy注解的方法被执行
- 销毁:如果实现了DisposableBean接口,destroy()方法被调用
java复制public class ExampleBean implements InitializingBean, DisposableBean {
@PostConstruct
public void init() {
System.out.println("@PostConstruct方法执行");
}
@Override
public void afterPropertiesSet() {
System.out.println("InitializingBean.afterPropertiesSet()执行");
}
@PreDestroy
public void cleanup() {
System.out.println("@PreDestroy方法执行");
}
@Override
public void destroy() {
System.out.println("DisposableBean.destroy()执行");
}
}
2.3 三级缓存解决循环依赖
Spring通过三级缓存机制解决了构造器注入无法处理的循环依赖问题。三级缓存分别是:
- singletonObjects:存放完全初始化好的单例Bean
- earlySingletonObjects:存放早期暴露的Bean(已实例化但未完成属性注入)
- singletonFactories:存放Bean工厂对象,用于生成早期引用
当Bean A依赖Bean B,而Bean B又依赖Bean A时,Spring的处理流程如下:
- 创建A实例,放入三级缓存
- 填充A的属性时发现需要B
- 创建B实例,放入三级缓存
- 填充B的属性时发现需要A
- 从三级缓存获取A的早期引用,注入到B
- B初始化完成,放入一级缓存
- A继续完成属性注入和初始化
- A初始化完成,放入一级缓存
注意:这种机制只适用于单例作用域的Bean,且只能解决属性注入的循环依赖。构造器注入的循环依赖无法通过这种方式解决,会导致BeanCurrentlyInCreationException。
3. AOP原理与实战应用
3.1 AOP核心概念解析
面向切面编程(AOP)是Spring的另一个核心特性,它允许开发者将横切关注点(如日志、事务、安全等)与业务逻辑分离。AOP的核心概念包括:
- 切面(Aspect):横切关注点的模块化,通常是一个包含通知和切点的类
- 连接点(Join point):程序执行过程中的特定点,如方法调用或异常抛出
- 通知(Advice):在特定连接点执行的动作,分为前置、后置、返回、异常和环绕五种类型
- 切点(Pointcut):匹配连接点的谓词,决定通知应该在何处执行
- 引入(Introduction):为现有类添加新的方法和属性
- 目标对象(Target object):被一个或多个切面通知的对象
- AOP代理:由AOP框架创建的对象,用于实现切面契约
3.2 基于注解的AOP实现
现代Spring应用通常使用@AspectJ注解风格实现AOP。以下是一个完整的切面示例:
java复制@Aspect
@Component
public class LoggingAspect {
@Pointcut("execution(* com.example.service.*.*(..))")
private void serviceLayer() {}
@Before("serviceLayer()")
public void logMethodStart(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("开始执行方法: " + methodName);
}
@AfterReturning(pointcut = "serviceLayer()", returning = "result")
public void logMethodSuccess(JoinPoint joinPoint, Object result) {
String methodName = joinPoint.getSignature().getName();
System.out.println("方法执行成功: " + methodName + ", 返回结果: " + result);
}
@AfterThrowing(pointcut = "serviceLayer()", throwing = "ex")
public void logMethodFailure(JoinPoint joinPoint, Exception ex) {
String methodName = joinPoint.getSignature().getName();
System.out.println("方法执行失败: " + methodName + ", 异常: " + ex.getMessage());
}
@Around("serviceLayer()")
public Object measureMethodExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = joinPoint.proceed();
long endTime = System.currentTimeMillis();
System.out.println("方法执行耗时: " + (endTime - startTime) + "ms");
return result;
}
}
3.3 AOP代理机制剖析
Spring AOP默认使用JDK动态代理实现接口的代理,对于没有实现接口的类则使用CGLIB生成子类代理。这种选择可以通过@EnableAspectJAutoProxy注解的proxyTargetClass属性控制:
java复制@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) // 强制使用CGLIB代理
public class AopConfig {
// 配置内容
}
JDK动态代理和CGLIB代理的主要区别:
- JDK代理:基于接口,使用Proxy.newProxyInstance()创建代理,性能较好
- CGLIB代理:基于继承,生成目标类的子类,可以代理没有接口的类,但创建速度较慢
在实际应用中,如果目标类实现了接口,Spring默认使用JDK代理;否则使用CGLIB。从Spring 4.0开始,CGLIB被包含在Spring核心模块中,不再需要额外依赖。
4. Spring事务管理机制
4.1 声明式事务实现原理
Spring的事务管理建立在AOP基础之上,通过@Transactional注解实现声明式事务。这个注解可以应用在类或方法级别,支持以下配置属性:
- propagation:事务传播行为(REQUIRED, SUPPORTS, MANDATORY等)
- isolation:事务隔离级别(DEFAULT, READ_UNCOMMITTED等)
- timeout:事务超时时间(秒)
- readOnly:是否只读事务
- rollbackFor/rollbackForClassName:触发回滚的异常类型
- noRollbackFor/noRollbackForClassName:不触发回滚的异常类型
java复制@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private InventoryRepository inventoryRepository;
@Transactional(propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT,
timeout = 30,
rollbackFor = {BusinessException.class})
public void placeOrder(Order order) {
// 检查库存
inventoryRepository.checkStock(order.getItems());
// 扣减库存
inventoryRepository.reduceStock(order.getItems());
// 创建订单
orderRepository.save(order);
}
}
4.2 事务传播行为详解
Spring定义了7种事务传播行为,理解这些行为对于设计正确的事务边界至关重要:
- REQUIRED(默认):如果当前存在事务,则加入该事务;否则新建一个事务
- SUPPORTS:如果当前存在事务,则加入该事务;否则以非事务方式执行
- MANDATORY:必须在一个已有事务中执行,否则抛出异常
- REQUIRES_NEW:新建一个事务,如果当前存在事务,则挂起当前事务
- NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,则挂起当前事务
- NEVER:以非事务方式执行,如果当前存在事务,则抛出异常
- NESTED:如果当前存在事务,则在嵌套事务中执行;否则行为同REQUIRED
一个常见的使用场景是日志记录。我们可能希望日志记录操作不参与业务事务,即使业务事务回滚,日志仍然需要保存:
java复制@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private AuditLogService auditLogService;
@Transactional
public void placeOrder(Order order) {
// 业务操作
processOrder(order);
// 记录审计日志(独立事务)
auditLogService.logOperation("placeOrder", order.getId());
}
}
@Service
public class AuditLogServiceImpl implements AuditLogService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logOperation(String operation, Long entityId) {
// 记录日志到数据库
}
}
4.3 事务失效的常见场景
在实际开发中,事务不生效是一个常见问题。以下是几种典型的事务失效场景:
- 方法访问权限不是public:Spring AOP代理无法拦截非public方法
- 自调用问题:同一个类中一个方法调用另一个@Transactional方法
- 异常类型不匹配:默认只对RuntimeException回滚,检查异常不会触发回滚
- 数据库引擎不支持:如MySQL的MyISAM引擎不支持事务
- 错误的事务管理器配置:多数据源时需要正确配置对应的事务管理器
解决自调用问题的一种方法是使用AopContext获取当前代理:
java复制@Service
public class OrderServiceImpl implements OrderService {
@Transactional
public void processOrder(Order order) {
// 处理订单
OrderServiceImpl proxy = (OrderServiceImpl) AopContext.currentProxy();
proxy.updateInventory(order); // 通过代理调用,事务生效
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateInventory(Order order) {
// 更新库存
}
}
提示:要使用AopContext,需要在配置类上添加@EnableAspectJAutoProxy(exposeProxy = true)注解。不过更推荐的做法是将事务方法拆分到不同的服务类中,这样代码结构更清晰。
