设计模式与Spring框架:IoC容器中的单例与工厂、AOP中的代理、MVC中的适配器
说实话,很多Java开发者天天用Spring,但未必清楚它背后到底用了哪些设计模式。我经常在面试中问这个问题,能说清楚的人真不多。今天咱们就聊聊Spring框架里最核心的三个设计模式:单例、工厂、代理和适配器。这些不是纸上谈兵,而是你每天写代码时都在用的东西。
一、IoC容器中的单例模式
Spring的IoC容器默认管理的Bean都是单例的。什么意思呢?就是同一个Bean定义,在整个容器中只会创建一个实例。我个人习惯把Spring容器想象成一个“对象仓库”,你每次要某个Bean,它都给你同一个对象。
核心要点:Spring的单例Bean默认是scope="singleton",这也是默认值。如果你需要每次获取新实例,可以改成scope="prototype"。
我在项目中遇到过一个问题:一个无状态的Service Bean,因为不小心在类里放了一个成员变量,结果在高并发下数据全乱了。嗯,这就是单例模式带来的“副作用”——所有线程共享同一个实例,成员变量就成了共享资源。
// Spring默认单例
@Component
public class UserService {
// 注意:单例模式下,这个list会被所有请求共享
private List<String> tempList = new ArrayList<>();
public void process(String data) {
tempList.add(data); // 并发问题!
// 业务逻辑...
}
}
避坑指南:我曾经在一个项目中,因为单例Bean里放了可变的成员变量,导致线上偶发数据错乱。排查了两天才找到原因。记住:单例Bean里只能放无状态的逻辑,或者用ThreadLocal来处理线程隔离的数据。
二、IoC容器中的工厂模式
Spring的BeanFactory和ApplicationContext,说白了就是工厂模式的经典实现。你不需要自己new对象,告诉Spring“我要什么”,它帮你创建好。这其实就是工厂模式的核心思想——把对象的创建和使用分离。
你想想看,如果没有工厂模式,你的代码会变成什么样?到处都是new XXX(),耦合度极高。Spring用工厂模式帮你管理对象的生命周期,你只需要关注业务逻辑。
// 传统方式:硬编码创建对象
UserService userService = new UserServiceImpl();
userService.doSomething();
// Spring方式:工厂模式创建
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.doSomething();
我个人觉得,Spring的工厂模式最妙的地方在于:它不只是简单的创建对象,还能处理对象之间的依赖关系。比如A依赖B,Spring在创建A的时候会自动注入B的实例。这在大型项目中简直是救命稻草。
三、AOP中的代理模式
AOP(面向切面编程)是Spring的另一个核心功能,它的底层实现就是代理模式。说白了,就是给目标对象创建一个“替身”,在调用目标方法前后插入额外的逻辑。
Spring AOP支持两种代理方式:
| 代理方式 | 适用场景 | 实现原理 |
|---|---|---|
| JDK动态代理 | 目标类实现了接口 | 通过反射生成代理类 |
| CGLIB代理 | 目标类没有实现接口 | 通过字节码增强生成子类 |
// 假设有一个事务切面
@Aspect
@Component
public class TransactionAspect {
@Around("@annotation(Transactional)")
public Object manageTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
// 前置逻辑:开启事务
System.out.println("开启事务");
Object result = joinPoint.proceed(); // 调用目标方法
// 后置逻辑:提交事务
System.out.println("提交事务");
return result;
}
}
经验之谈:我在项目中遇到过一个问题:某个Service类没有实现接口,结果用JDK动态代理报错了。后来改成CGLIB代理才解决。所以如果你的类没有实现接口,记得在配置里加上proxy-target-class="true"。
四、MVC中的适配器模式
Spring MVC里的适配器模式,主要体现在HandlerAdapter上。你想想看,Controller的写法五花八门:有的返回ModelAndView,有的返回String,有的用@ResponseBody返回JSON。Spring怎么统一处理这些不同的Controller呢?答案就是适配器模式。
每个Controller都会被包装成一个适配器,适配器知道怎么调用这个Controller,并把结果转换成统一的格式。这样Spring MVC的核心代码就不用关心Controller的具体实现了。
// HandlerAdapter接口
public interface HandlerAdapter {
boolean supports(Object handler);
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}
// 针对不同Controller的适配器实现
public class RequestMappingHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return handler instanceof HandlerMethod;
}
@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 调用Controller方法,处理返回值
// ...
}
}
说白了,适配器模式就是“让不兼容的接口能够一起工作”。Spring MVC通过适配器,把各种风格的Controller统一成了标准的处理流程。我在项目中曾经自己写过一个自定义的Controller类型,就是通过实现HandlerAdapter来让它被Spring MVC识别的。
五、知识体系总览
为了让你更直观地理解这些设计模式在Spring中的位置,我画了一张图:
六、总结与思考
Spring框架之所以强大,很大程度上是因为它巧妙地运用了多种设计模式。单例模式保证了对象的唯一性和资源的高效利用;工厂模式解耦了对象的创建和使用;代理模式让AOP切面编程成为可能;适配器模式让MVC架构能够灵活处理各种Controller。
我个人觉得,学习设计模式最好的方式就是去读优秀框架的源码。Spring就是一个绝佳的教材。你不需要一下子全看懂,但每次看都能有新的收获。比如我当年看Spring的BeanFactory源码时,才真正理解了工厂模式在大型项目中的威力。
一句话总结:设计模式不是空中楼阁,它就藏在你的日常代码里。Spring框架就是设计模式最好的“活教材”。
公众号:蓝海资料掘金营,微信deep3321