在日常开发中,Spring 已经成了 Java 后端开发的标配。不管是写个简单的接口,还是搭建微服务,Spring 都能帮你省不少事。但你有没有想过,为什么它这么“顺手”?其实背后离不开一系列经典设计模式的支撑。
工厂模式:把对象创建交给 Spring
平时我们创建对象,通常是 new 一个实例。但在 Spring 中,更多是通过配置或注解让容器来创建 Bean。这就是典型的工厂模式。比如你写了个 UserService:
@Service
public class UserService {
public void sayHello() {
System.out.println("Hello, Spring!");
}
}
Spring 容器会根据 @Service 注解自动创建这个类的实例,你只需要用 @Autowired 注入就能用。相当于把“生产对象”的活儿交给了工厂(也就是 IOC 容器),代码更干净,也更容易管理。
代理模式:AOP 的核心实现
你想给方法加个日志、统计执行时间,或者做权限校验,总不能每个方法都手动写一遍吧?Spring AOP 就解决了这个问题,而它的底层靠的就是代理模式。
比如你写了个切面:
@Aspect
@Component
public class LogAspect {
@Before("execution(* com.example.service.*.*(..))")
public void before() {
System.out.println("方法执行前打个日志");
}
}
Spring 会在运行时动态生成一个代理对象,拦截目标方法的调用。你调的是“假对象”,但执行效果却是真实的,还额外加了日志逻辑。就像你点外卖,表面是和平台打交道,其实是平台帮你调度餐厅、骑手,整个过程被“代理”了。
模板方法模式:统一流程,留出扩展点
Spring JDBC 的 JdbcTemplate 就是个典型例子。数据库操作有固定流程:获取连接、创建语句、执行 SQL、处理结果、关闭资源。但每次执行的 SQL 和结果处理又不一样。
于是 Spring 定义了一个模板,把不变的部分封装好,只让你关心变化的部分:
String sql = "SELECT name FROM user WHERE id = ?";
String name = jdbcTemplate.queryForObject(sql, String.class, 1);
你看,不用写 try-catch 关闭连接,也不用手动处理 ResultSet,Spring 把流程控好了,你只需要告诉它 SQL 和参数。这就是模板方法模式的妙处:流程统一,细节可变。
观察者模式:事件机制的基石
系统里经常需要“做完一件事后通知其他人”。比如用户注册成功后,要发邮件、发短信、记录日志。如果全都写在一起,代码会越来越臃肿。
Spring 提供了事件机制,你可以发布一个事件,让多个监听器去响应:
// 发布事件
applicationContext.publishEvent(new UserRegisteredEvent(user));
// 监听器
@EventListener
public void sendEmail(UserRegisteredEvent event) {
System.out.println("发送欢迎邮件给:" + event.getUser().getName());
}
这样一来,主流程只管注册,其他动作由监听器异步处理。就像你下单后,系统自动通知仓库发货、财务记账、客服准备跟进,各司其职,互不干扰。
单例模式:默认的 Bean 作用域
Spring 中的 Bean 默认是单例的。也就是说,不管你注入多少次,拿到的都是同一个实例。这不仅能节省内存,还能保证全局状态一致。
比如你有个缓存服务:
@Component
public class CacheService {
private Map<String, Object> cache = new ConcurrentHashMap<>();
public void put(String key, Object value) {
cache.put(key, value);
}
}
所有地方注入 CacheService,操作的都是同一份缓存数据。这就是单例模式的实际应用——大家共用一个实例,避免重复创建和数据不一致的问题。
这些设计模式不是孤立存在的,它们在 Spring 里相互配合,才让框架既灵活又稳定。理解它们,不只是为了面试背题,更是为了写出更清晰、更易维护的代码。下次写代码的时候,不妨多想想:我是不是也在无意中用了某种模式?