第一次接触Spring框架时,我完全被各种术语搞晕了——IoC、DI、AOP、Bean...直到有一天,我把Spring想象成一个智能管家,一切突然变得清晰起来。这个管家不仅会帮你整理房间(管理对象),还能在你需要时递上咖啡(依赖注入),甚至自动打扫卫生(AOP切面)。这种开发体验的转变,正是Spring风靡Java开发领域20余年的核心原因。
Spring本质上是一个轻量级的对象容器,它颠覆了传统Java开发中"new对象-调方法"的直白模式。想象一下:以前你要喝咖啡,得自己去买咖啡豆、磨粉、冲泡(对应代码中的new对象、维护依赖关系);而现在,你只需要说"我要一杯美式"(@Autowired),管家就会把现成的咖啡送到你手上。这种转变就是控制反转(IoC)的精髓——把对象的创建和管理权从程序员手中转移到框架容器。
关键理解:Spring不是替代Java语法,而是改变了对象的管理方式。你仍然需要定义类和方法,但不再需要关心它们如何被创建和组装。
IoC(Inversion of Control)这个术语听起来很学术,其实概念非常简单。让我们用餐厅点餐来类比:
在代码层面,这意味着:
java复制// 传统方式
UserService userService = new UserServiceImpl();
userDao = new UserDaoImpl();
userService.setUserDao(userDao);
// Spring方式
@Autowired
private UserService userService; // 容器自动装配
Spring主要通过三种方式实现依赖注入:
java复制@Autowired
private UserRepository userRepository;
java复制private final UserRepository userRepository;
@Autowired // Spring 4.3+可省略
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
java复制private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
实战建议:新项目优先使用构造器注入,它明确声明了必需依赖,且便于单元测试。字段注入虽然简洁,但容易导致循环依赖问题。
Spring通过组件扫描(Component Scan)机制发现和管理Bean。常用注解包括:
| 注解 | 适用场景 | 特殊功能 |
|---|---|---|
| @Component | 通用组件 | 无 |
| @Service | 业务逻辑层 | 无,但语义更明确 |
| @Repository | 数据访问层 | 自动转换持久化异常 |
| @Controller | Web控制器层 | 处理HTTP请求 |
| @Configuration | 配置类 | 包含@Bean定义方法 |
配置组件扫描的基础示例:
java复制@Configuration
@ComponentScan("com.yourpackage") // 扫描该包及其子包
public class AppConfig {
// 可以额外定义@Bean方法
}
Spring支持多种作用域,最常用的是:
Singleton(单例):
Prototype(原型):
@Scope("prototype")Web相关作用域:
作用域配置示例:
java复制@Service
@Scope("prototype") // 或ConfigurableBeanFactory.SCOPE_PROTOTYPE
public class ShoppingCart {
// 每次注入都是新实例
}
AOP(面向切面编程)就像给代码"贴便利贴":你不需要修改原有代码,就能添加新功能。主要概念包括:
下面是一个增强版的日志切面,包含方法签名记录和执行时间统计:
java复制@Aspect
@Component
public class MethodLoggerAspect {
private static final Logger logger = LoggerFactory.getLogger(MethodLoggerAspect.class);
// 匹配service包下所有类的public方法
@Pointcut("execution(public * com.example.service..*(..))")
public void serviceMethods() {}
@Around("serviceMethods()")
public Object logMethodExecution(ProceedingJoinPoint pjp) throws Throwable {
String methodName = pjp.getSignature().toShortString();
Object[] args = pjp.getArgs();
logger.info("→ 进入方法: {} 参数: {}", methodName, Arrays.toString(args));
long startTime = System.currentTimeMillis();
Object result = pjp.proceed();
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("← 退出方法: {} 耗时: {}ms 结果: {}",
methodName, elapsedTime, result);
return result;
}
}
| 通知类型 | 执行时机 | 典型应用场景 |
|---|---|---|
| @Before | 方法执行前 | 参数校验、权限检查 |
| @After | 方法执行后(无论成败) | 资源清理、通知发送 |
| @AfterReturning | 方法成功返回后 | 结果日志、缓存更新 |
| @AfterThrowing | 方法抛出异常后 | 异常处理、事务回滚 |
| @Around | 包围整个方法 | 性能监控、事务管理 |
Spring事务管理的核心是@Transactional注解。完整配置示例:
java复制@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private InventoryService inventoryService;
@Transactional(
propagation = Propagation.REQUIRED, // 默认值:支持当前事务,没有则新建
isolation = Isolation.DEFAULT, // 使用数据库默认隔离级别
timeout = 30, // 超时时间(秒)
rollbackFor = Exception.class, // 对Exception异常回滚
noRollbackFor = BusinessException.class // 业务异常不回滚
)
public void placeOrder(Order order) throws BusinessException {
orderRepository.save(order);
inventoryService.reduceStock(order.getItems());
// 其他业务逻辑...
}
}
Spring定义了7种传播行为,最常用的有:
REQUIRED(默认):
REQUIRES_NEW:
NESTED:
SUPPORTS:
注解加在非public方法上:
自调用问题:
java复制public void methodA() {
this.methodB(); // 直接调用不走代理,事务失效
}
@Transactional
public void methodB() {...}
异常被捕获未抛出:
java复制try {
transactionalMethod();
} catch (Exception e) {
// 捕获后不抛出,事务不会回滚
}
默认只对RuntimeException回滚:
rollbackFor指定其他异常类型数据库引擎不支持事务:
多数据源未配置事务管理器:
传播行为配置不当:
方法final或static:
分层扫描:
java复制@SpringBootApplication
@ComponentScan(basePackages = "com.example")
@EntityScan("com.example.entity")
@EnableJpaRepositories("com.example.repository")
public class Application {...}
多环境配置:
spring.profiles.active激活外部化配置:
properties复制# 数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/db
spring.datasource.username=root
spring.datasource.password=secret
懒加载Bean:
java复制@Lazy
@Service
public class HeavyService {...}
合理使用作用域:
AOP切入点优化:
execution(* com..*(..))会扫描过多方法循环依赖处理:
@Lazy打破循环NoSuchBeanDefinitionException:
BeanCurrentlyInCreationException:
@Lazy或重构代码TransactionRequiredException:
NullPointerException:
虽然本文聚焦Spring核心,但了解其与现代Spring Boot的关系很重要:
自动配置:
spring-boot-autoconfigure实现起步依赖:
spring-boot-starter-web嵌入式容器:
Actuator:
Spring Boot不是替代Spring,而是让它更易用。所有核心概念(IoC、DI、AOP)仍然适用,只是配置方式更加现代化。
在最近的一个电商项目中,我们遇到了一个典型的事务问题:用户下单时需要同时更新订单表、库存表和积分表。最初的实现是这样的:
java复制public void createOrder(OrderDTO dto) {
// 非事务方法
orderService.save(dto);
inventoryService.reduce(dto.getItems()); // 可能失败
pointsService.add(dto.getUserId(), dto.getPoints()); // 可能失败
}
这会导致数据不一致——可能出现订单创建成功但库存未扣减的情况。解决方案是:
java复制@Transactional
public void createOrder(OrderDTO dto) {...}
java复制@Service
public class InventoryService {
@Transactional(propagation = Propagation.MANDATORY)
public void reduce(List<Item> items) {...}
}
java复制@Transactional(rollbackFor = {InventoryException.class, PointsException.class})
public void createOrder(OrderDTO dto) throws BusinessException {...}
最终实现保证了三个操作的原子性,要么全部成功,要么全部回滚。这个案例展示了Spring事务管理的实际价值。