从零手搓一个类Spring框架,彻底搞懂Spring核心原理
作者:JarryLi
适合读者:Java工程师以及对设计模式和框架原理感兴趣的同学。如果你想学习和运用设计模式,熟悉Spring框架是最佳的途径之一。
前言
还记得很多年前刚接触Spring的时候,我觉得很神奇:通过xml配置就可以自动注入对象,甚至一个@Autowired注解就能搞定?AOP 还能在不侵入业务代码的情况下统一加日志和事务?那时主流还是 EJB 体系,使用原生Servlet、JSP以及Struts框架。面对 IoC、DI 这些看似"魔法"的能力,不免想一探究竟:它们到底是怎么实现的?
从那时起,开始学习Spring源码。真正看进去才发现,这些并不神奇,而是大量工程化设计的结果:层层抽象、接口解耦、经典设计模式的复用。源码规模不小,很难一口气看完,只能带着问题一点点学习。
纸上得来终觉浅,绝知此事要躬行。于是参照Spring框架写了一个简化版的SpringWind框架,用最简洁的代码实现 Spring 的核心功能,让每个想深入理解框架原理的开发者都能看懂。
在这个过程中,不仅搞懂了IoC、DI、AOP这些核心概念,以及Bean生命周期、DispatcherServlet控制器与ApplicationContext容器体系,还深刻理解了为什么Spring要这样设计。现在,我把这些实践经验和心得分享出来,希望能帮助你快速掌握Spring的核心原理。
代码仓库: https://github.com/microwind/design-patterns/tree/main/practice-projects/springwind
目录
- 关于 SpringWind 框架
- Spring的设计理念
- Spring的核心特性
- 深入理解Spring框架
- 参照Spring框架设计Springwind的架构
- SpringWind 与 Spring MVC 的对比
- Springwind框架有什么用
- Springwind的项目结构
- Springwind使用实践
- 快速开始
- Springwind改进点
关于 SpringWind 框架
SpringWind 是一个教学性质的轻量级 Java Web 框架,旨在帮助开发者深入理解 Spring 框架的核心原理。通过从零开始实现 Spring 的核心机制(IoC、DI、AOP、三级缓存、MVC等),让框架不再是"黑盒子",而是可以完全理解的"白盒子"。
框架定位
SpringWind 不是为了替代 Spring Framework,而是作为:
- 学习工具:是理解 Spring 核心原理的最佳实践
- 教学框架:通过由浅入深的方式,让你逐渐理解Spring框架
- 快速原型:对于一些小型项目,那么Springwind更加轻量简便
- 探索平台:可以验证一些新架构思想与理念,不断创新
核心特性
SpringWind 实现了 Spring Framework 的以下核心功能:
特性说明对应 Spring 功能IoC 容器自动扫描、注册和管理 Bean 的生命周期ApplicationContext依赖注入通过 @Autowired 实现自动装配Dependency Injection组件注解@Component、@Service、@Controller、@RepositoryStereotype AnnotationsMVC 模式模拟 Spring MVC 的请求处理机制Spring MVC请求映射@RequestMapping 实现 URL 到方法的映射Request Mapping参数绑定@PathVariable、@RequestParam、@RequestBodyParameter BindingAOP 支持基于 JDK/CGLIB 的动态代理实现切面编程Spring AOPBeanPostProcessorBean 的前置和后置处理,支持生命周期干预BeanPostProcessorSmartInstantiationAwareBeanPostProcessor支持在 Bean 实例化早期创建代理,解决循环依赖SmartInstantiationAwareBeanPostProcessor三级缓存优雅解决循环依赖问题Circular Dependency ResolutionJDBC 模板简化数据库操作的模板类JdbcTemplateJSON 响应自动序列化对象为 JSON@ResponseBody与 Spring Framework 的关系
SpringWind 参考了 Spring Framework 的设计思想,但做了大量简化:
相同点:
- 核心设计理念(IoC、DI、AOP)
- 注解驱动开发
- 分层架构(Controller-Service-Dao)
- 三级缓存解决循环依赖
- 模板方法封装样板代码
不同点:
- 代码规模:SpringWind 代码约 4000 行,Spring Framework 超过 50 万行
- 功能范围:SpringWind 聚焦核心功能,Spring 提供全面的企业级特性
- 复杂度:SpringWind 去除了大量抽象层次,更易理解
- 生产就绪:Spring 经过大规模验证,SpringWind 主要用于学习
技术栈
- 核心技术:
- ├── Java 17+<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /># 现代 Java 特性
- ├── Jakarta Servlet API 6.1<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /> # Web 容器标准
- ├── CGLIB 3.3.0<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /> # 字节码增强(AOP)
- ├── SLF4J + Logback<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /> # 日志框架
- └── Jackson 2.18.2<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /> # JSON 序列化
- 构建工具:
- └── Maven 3.6+
- 运行环境:
- ├── Embedded Tomcat 11<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /> # 嵌入式 Web 服务器
- └── H2 / MySQL<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /> # 数据库(可选)
复制代码 SpringWind 与 Spring MVC 注解对照表
为了降低学习成本,SpringWind 的注解与 Spring MVC 保持一致:
SpringWind 注解Spring MVC 注解作用示例@Component@Component标记通用组件@Component@Service@Service标记服务层组件@Service@Controller@Controller标记控制器组件@Controller@Repository@Repository标记数据访问层组件@Repository@Autowired@Autowired自动注入依赖@Autowired private UserDao dao;@RequestMapping@RequestMapping映射 HTTP 请求@RequestMapping("/user")@PathVariable@PathVariable绑定路径变量@PathVariable("id") Long id@RequestParam@RequestParam绑定请求参数@RequestParam("name") String name@RequestBody@RequestBody绑定请求体(JSON)@RequestBody User user@ResponseBody@ResponseBody返回 JSON 响应@ResponseBody@Aspect@Aspect标记切面类@Aspect@Before@Before前置通知@Before("com.example.*")@After@After后置通知@After("com.example.*")@Around@Around环绕通知@Around("com.example.*")使用示例:- // 定义 Controller
- @Controller
- @RequestMapping("/user")
- public class UserController {
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />@Autowired
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />private UserService userService; // 自动注入
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />@RequestMapping("/get")
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />@ResponseBody
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public User getUser(@RequestParam("id") Long id) {
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return userService.getUserById(id);
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />@RequestMapping("/detail/{id}")
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />@ResponseBody
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public User getUserDetail(@PathVariable("id") Long id) {
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return userService.getUserById(id);
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />@RequestMapping("/create")
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />@ResponseBody
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public User createUser(@RequestBody User user) {
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return userService.createUser(user);
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}
- }
复制代码 如果你熟悉 Spring MVC,就能零学习成本理解 SpringWind!你理解了Springwind也就明白了Spring。
学习路径建议
第一步:快速体验(10分钟)
- 运行示例项目,看看效果
- 访问几个 API 接口,理解请求流程
第二步:理解核心原理(1-2小时)
- 阅读本文档的"Spring的设计理念"和"Spring的核心特性"
- 打断点调试,观察 Bean 的创建和依赖注入过程
- 理解三级缓存如何解决循环依赖
第三步:深入源码(3-5小时)
- 阅读 SpringWindApplicationContext.java(IoC 容器核心)
- 阅读 AspectProcessor.java(AOP 代理创建)
- 阅读 DispatcherServlet.java(MVC 请求分发)
- 阅读 JdbcTemplate.java(JDBC 模板方法)
第四步:动手实践(1-2天)
- 基于 SpringWind 写一个小项目
- 尝试添加新功能(如事务支持)
- 对比 Spring Framework 源码,理解设计差异
第五步:给 Spring 贡献代码(进阶)
- 理解 Spring 的设计思想后,可以参与 Spring 社区
- 提交 Bug 修复或新功能的 Pull Request
Spring的设计理念
在开始造轮子之前,我们得先理解Spring为什么要这样设计。很多人觉得Spring框架复杂,但其实它的核心理念非常简单。
1. 控制反转(IoC)- 别再自己new对象了
传统做法的痛点:- // 传统方式:自己管理对象
- public class UserService {
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />private UserDao userDao = new UserDaoImpl(); // 硬编码依赖
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />private EmailService emailService = new EmailServiceImpl(); // 硬编码依赖
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public void registerUser(User user) {
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />userDao.save(user);
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />emailService.sendWelcomeEmail(user);
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}
- }
复制代码 这样写有什么问题?
其实这样写本身从理解来讲最好理解,但不便于扩展。项目一大,后续维护就变得麻烦。
- 耦合度高:UserService强依赖具体的实现类
- 难以测试:无法替换成Mock对象做单元测试
- 配置混乱:数据库连接、邮件配置都硬编码在代码里
Spring的解决方案:- // Spring方式:依赖注入
- @Service
- public class UserService {
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />@Autowired
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />private UserDao userDao; // 由容器注入
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />@Autowired
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />private EmailService emailService;
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public void registerUser(User user) {
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />userDao.save(user);
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />emailService.sendWelcomeEmail(user);
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}
- }
复制代码 现在,对象的创建和管理交给了IoC容器,业务代码只需要声明"我需要什么",而不用关心"怎么创建"。这就是控制反转的核心思想。
2. 面向切面编程(AOP)- 横切关注点分离
假设你要给所有Service方法加日志,传统做法是这样:- public class UserService {
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public void registerUser(User user) {
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />System.out.println("开始执行registerUser"); // 日志代码
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />long start = System.currentTimeMillis();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /> // 性能监控代码
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />userDao.save(user); // 真正的业务逻辑
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />long end = System.currentTimeMillis();
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />System.out.println("执行完成,耗时: " + (end - start) + "ms");
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}
- }
复制代码 业务代码和日志、监控代码混在一起,非常臃肿。而且如果有100个方法,就得重复100遍这些代码。
AOP的解决方案:- @Aspect
- public class LoggingAspect {
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />@Before("com.example.service.*.*")
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public void logBefore(Method method) {
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />System.out.println("开始执行: " + method.getName());
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />@After("com.example.service.*.*")
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public void logAfter(Method method) {
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />System.out.println("执行完成: " + method.getName());
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}
- }
- // 业务代码保持纯净
- @Service
- public class UserService {
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public void registerUser(User user) {
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />userDao.save(user); // 只关注业务逻辑
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}
- }
复制代码 日志、事务、权限校验这些横切关注点被抽离出来,业务代码变得清爽多了。
3. 约定优于配置 - 少写XML,多用注解
早期的Spring需要写大量XML配置:- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />
复制代码 现在只需要:- @Servicepublic class UserService {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />@Autowired<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />private UserDao userDao;}
复制代码 一个注解就搞定了。这就是约定优于配置的威力。
4. 模板方法模式 - 封装样板代码
JDBC操作有大量的样板代码:- // 传统JDBC:50多行代码Connection conn = null;PreparedStatement stmt = null;ResultSet rs = null;try {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />conn = dataSource.getConnection();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />stmt = conn.prepareStatement("SELECT * FROM users WHERE id = ?");<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />stmt.setLong(1, userId);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />rs = stmt.executeQuery();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />if (rs.next()) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return new User(rs);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}} catch (SQLException e) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />throw new RuntimeException(e);} finally {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />if (rs != null) try { rs.close(); } catch (SQLException e) {}<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />if (stmt != null) try { stmt.close(); } catch (SQLException e) {}<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />if (conn != null) try { conn.close(); } catch (SQLException e) {}}
复制代码 Spring JdbcTemplate:- // 简洁的JdbcTemplate:2行代码User user = jdbcTemplate.queryForObject(<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />"SELECT * FROM users WHERE id = ?",<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />(rs, rowNum) -> new User(rs),<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />userId);
复制代码 获取连接、关闭资源这些固定步骤被封装了,你只需要专注于SQL和结果映射。
Spring的核心特性
在理解了Spring的设计理念后,我们来看看它的核心特性是如何实现的。
IoC容器 - 对象的大管家
IoC容器就像一个对象工厂,负责:
- 扫描组件:找到所有带@Component、@Service等注解的类
- 注册Bean定义:把类的元信息存起来
- 实例化Bean:通过反射创建对象
- 依赖注入:把依赖的对象注入进去
- 生命周期管理:管理Bean的创建和销毁
Bean的生命周期:
---title: Bean的生命周期(完整流程)---graph LR<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />A[扫描组件] --> B[注册BeanDefinition]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />B --> C[实例化Bean
调用构造器]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />C --> D["放入三级缓存
ObjectFactory
关键!"]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />D --> E["依赖注入
@Autowired字段
可能触发循环依赖"]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />E --> F["前置处理器
postProcessBefore
Initialization"]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />F --> G["初始化方法
@PostConstruct"]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />G --> H["后置处理器
postProcessAfter
Initialization
AOP代理创建"]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />H --> I["放入一级缓存
清理二三级缓存"]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />I --> J[Bean就绪
可被使用]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />J --> K["销毁
@PreDestroy"]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style A fill:#e1f5fe,stroke:#01579b,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style B fill:#fff3e0,stroke:#f57c00,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style C fill:#f1f8e9,stroke:#7cb342,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style D fill:#fff8e1,stroke:#ffb300,stroke-width:3px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style E fill:#fce4ec,stroke:#c2185b,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style F fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style G fill:#e8f5e8,stroke:#388e3c,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style H fill:#ffe0b2,stroke:#e65100,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style I fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style J fill:#a5d6a7,stroke:#00695c,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style K fill:#FFE4E1,stroke:#B20E7E,stroke-width:2px关键步骤说明:
- 步骤4(三级缓存):这是解决循环依赖的关键,放入的是ObjectFactory而非Bean本身
- 步骤5(依赖注入):如果依赖的Bean正在创建,会从三级缓存获取早期引用,触发循环依赖解决机制
- 步骤8(后置处理器):AOP代理通常在此创建,但如果在步骤4的getEarlyBeanReference中已创建,则跳过
在SpringWind中,我们用一个核心类SpringWindApplicationContext来实现这些功能:- public class SpringWindApplicationContext {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 一级缓存:完整初始化的Bean对象<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />private Map singletonObjects = new ConcurrentHashMap();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 二级缓存:早期Bean引用(用于解决循环依赖)<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />private Map earlySingletonObjects = new ConcurrentHashMap();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 三级缓存:ObjectFactory工厂(用于延迟生成早期引用,支持AOP)<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />private Map clazz = bean.getClass();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 扫描所有字段<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />for (Field field : clazz.getDeclaredFields()) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />if (!field.isAnnotationPresent(Autowired.class)) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />continue;<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 根据类型从容器中查找Bean<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />Object dependency = getBean(field.getType());<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />if (dependency == null) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />throw new BeanNotFoundException("找不到依赖: " + field.getType());<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 通过反射设置字段值<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />field.setAccessible(true);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />field.set(bean, dependency);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}}
复制代码 流程很简单:
- 找到所有带@Autowired的字段
- 根据字段类型从容器中查找对应的Bean
- 通过反射把Bean注入进去
AOP - 动态代理的实现
AOP的核心是动态代理。SpringWind支持两种代理方式:
1. JDK动态代理(基于接口)- // 如果目标类实现了接口,目标类实现的接口数量大于0if (interfaces.length > 0) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 使用JDK动态代理创建代理对象<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return Proxy.newProxyInstance(<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 指定类加载器,用于加载动态生成的代理类<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />classLoader,<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 代理类需要实现的接口数组,代理对象需要实现接口的所有方法<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />interfaces,<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 创建调用处理器,将目标对象和切面列表传入<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// AopInvocationHandler负责处理方法调用时的切面逻辑<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />new AopInvocationHandler(target, aspects)<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />);}
复制代码 2. CGLIB代理(基于继承)- // 创建A的过程(对应代码SpringWindApplicationContext.java)
- 1. getBean("beanA") 被调用
- 2. 检查一级缓存singletonObjects → 未找到
- 3. 检查是否正在创建 → 不在,继续创建流程
- 4. 标记A正在创建:singletonsCurrentlyInCreation.add("beanA")
- 5. 实例化A:调用构造器,得到原始对象(此时尚未注入依赖)
- 6. **关键步骤**:将ObjectFactory放入三级缓存
- singletonFactories.put("beanA", () -> getEarlyBeanReference("beanA", rawBeanA))
- // 注意:此时放入的是工厂对象,不是Bean本身!
- 7. 开始为A注入依赖:doDependencyInjection(beanA)
- 8. 发现A依赖B,调用getBean("beanB")
- // 创建B的过程(嵌套在A的创建过程中)
- 9. 检查一级缓存 → 未找到B
- 10. 标记B正在创建:singletonsCurrentlyInCreation.add("beanB")
- 11. 实例化B:得到原始对象
- 12. 将ObjectFactory放入三级缓存:
- singletonFactories.put("beanB", () -> getEarlyBeanReference("beanB", rawBeanB))
- 13. 开始为B注入依赖:发现B依赖A,调用getBean("beanA")
- // 循环依赖检测与解决(关键!)
- 14. 检查一级缓存 → 未找到A(因为A还没完成初始化)
- 15. 发现A在正在创建集合中:singletonsCurrentlyInCreation.contains("beanA") = true
- 16. 检查二级缓存earlySingletonObjects → 未找到
- 17. 检查三级缓存singletonFactories → 找到A的ObjectFactory ✓
- 18. **调用ObjectFactory.getObject()**
- → 内部调用getEarlyBeanReference("beanA", rawBeanA)
- → 如果A需要AOP代理,SmartInstantiationAwareBeanPostProcessor会在此创建代理
- → 返回A的早期引用(可能是原始对象,也可能是代理对象)
- 19. 将A的早期引用放入二级缓存:earlySingletonObjects.put("beanA", earlyA)
- 20. 从三级缓存移除:singletonFactories.remove("beanA")
- 21. 返回A的早期引用给B ✓
- 22. B完成依赖注入(注入了A的早期引用)
- 23. B执行前置处理器:applyBeanPostProcessorsBeforeInitialization
- 24. B执行@PostConstruct初始化方法
- 25. B执行后置处理器:applyBeanPostProcessorsAfterInitialization(AOP代理可能在此创建)
- 26. B完成创建,放入一级缓存:singletonObjects.put("beanB", beanB)
- 27. 清理B的二、三级缓存
- 28. 从正在创建集合移除:singletonsCurrentlyInCreation.remove("beanB")
- 29. getBean("beanB") 返回完整的B → A成功注入B
- 30. A完成依赖注入
- 31. A执行前置处理器
- 32. A执行@PostConstruct初始化方法
- 33. A执行后置处理器(如果A之前已在getEarlyBeanReference中创建了代理,这里会跳过)
- 34. A完成创建,放入一级缓存:singletonObjects.put("beanA", beanA)
- 35. 清理A的二、三级缓存
- 36. 从正在创建集合移除:singletonsCurrentlyInCreation.remove("beanA")
- 37. 完成!A和B都成功创建,循环依赖已解决
复制代码 代理的工作流程:
---title: AOP方法调用流程---flowchart LR<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />A["客户端调用方法"] --> B["代理对象拦截"]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />B --> C["执行@Before通知
(前置增强)"]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />C --> D["调用目标方法
(真正的业务逻辑)"]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />D --> E["执行@After通知
(后置增强)"]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />E --> F["返回结果"]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" /><bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />%% 样式定义<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style A fill:#e1f5fe,stroke:#01579b,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style B fill:#fff3e0,stroke:#f57c00,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style C fill:#f1f8e9,stroke:#7cb342,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style D fill:#fff8e1,stroke:#ffb300,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style E fill:#fce4ec,stroke:#c2185b,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style F fill:#e8f5e8,stroke:#388e3c,stroke-width:2pxWeb MVC - 前端控制器模式
SpringWind的Web MVC实现了经典的前端控制器模式:- @WebServlet("/*")public class DispatcherServlet extends HttpServlet {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />protected void service(HttpServletRequest req, HttpServletResponse resp) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 1. 获取请求路径<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />String uri = req.getRequestURI();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 2. 查找处理器<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />HandlerMethod handler = getHandler(uri);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 3. 解析参数<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />Object[] args = resolveArguments(handler, req, resp);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 4. 调用Controller方法<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />Object result = handler.invoke(args);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 5. 处理响应<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />handleResult(result, resp);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}}
复制代码 请求处理流程:
graph LR<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />A[HTTP请求] --> B[DispatcherServlet]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />B --> C[URL映射]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />C --> D[参数解析]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />D --> E[调用Controller]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />E --> F{返回类型判断}<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />F -->|返回View名称| G[视图解析]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />F -->|返回数据| H[消息转换]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />G --> I[视图渲染]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />H --> I<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />I --> J[HTTP响应]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" /><bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style A fill:#90EE90,stroke:#333,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style B fill:#ADD8E6,stroke:#333,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style C fill:#FFB6C1,stroke:#333,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style D fill:#DDA0DD,stroke:#333,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style E fill:#FFD700,stroke:#333,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style F fill:#F0E68C,stroke:#333,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style G fill:#87CEEB,stroke:#333,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style H fill:#98FB98,stroke:#333,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style I fill:#FFA07A,stroke:#333,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style J fill:#90EE90,stroke:#333,stroke-width:2pxJDBC模板 - 样板代码的终结者
JdbcTemplate只是把基础JDBC操作的步骤封装起来,实现起来非常简单,没有ORM框架那么复杂:- public T queryForObject(String sql, RowMapper mapper, Object... args) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />Connection conn = null;<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />PreparedStatement stmt = null;<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />ResultSet rs = null;<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />try {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />conn = getConnection();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 1. 获取连接<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />stmt = prepareStatement(sql); // 2. 创建语句<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />setParameters(stmt, args);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /> // 3. 设置参数<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />rs = stmt.executeQuery();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /> // 4. 执行查询<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return mapper.mapRow(rs, 0); // 5. 映射结果(你只需要关心这一步)<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />} catch (SQLException e) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />throw new RuntimeException(e);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />} finally {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />closeResources(rs, stmt, conn); // 6. 关闭资源<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}}
复制代码 你只需要提供SQL和结果映射逻辑,其他的都由框架处理。
深入理解Spring框架
为什么要用注解?
注解本质上是元数据,用来描述代码的特性。Java通过反射API可以在运行时读取这些注解:- // 扫描类上的注解Class clazz = Class.forName("com.example.UserService");if (clazz.isAnnotationPresent(Service.class)) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 这是一个Service,注册为Bean}// 扫描字段上的注解for (Field field : clazz.getDeclaredFields()) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />if (field.isAnnotationPresent(Autowired.class)) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 这个字段需要依赖注入<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}}
复制代码 注解相比XML配置的优势:
- 就近原则:配置和代码在一起,更直观
- 类型安全:编译期检查,避免拼写错误
- 重构友好:IDE可以自动重构
反射 - Spring的基石
Spring大量使用反射来实现框架功能:- // 如果目标类实现了接口,目标类实现的接口数量大于0
- if (interfaces.length > 0) {
- // 使用JDK动态代理创建代理对象
- return Proxy.newProxyInstance(
- // 指定类加载器,用于加载动态生成的代理类
- classLoader,
- // 代理类需要实现的接口数组,代理对象需要实现接口的所有方法
- interfaces,
- // 创建调用处理器,将目标对象和切面列表传入
- // AopInvocationHandler负责处理方法调用时的切面逻辑
- new AopInvocationHandler(target, aspects)
- );
- }
复制代码 反射的性能确实比直接调用慢,但Spring通过缓存来优化:- // 构造器缓存private Map> constructorCache = new ConcurrentHashMap();public Object createBean(Class clazz) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />Constructor constructor = constructorCache.computeIfAbsent(clazz, c -> {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return c.getDeclaredConstructor();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />});<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return constructor.newInstance();}
复制代码 设计模式 - Spring是经典设计模式的忠实拥趸
Spring框架中应用了大量的设计模式:
设计模式应用场景举例工厂模式Bean的创建ApplicationContext就是Bean工厂单例模式Bean的作用域默认的单例Bean代理模式AOP实现JDK动态代理、CGLIB代理模板方法模式固定流程封装JdbcTemplate、RestTemplate策略模式不同的实现策略RowMapper结果映射观察者模式事件机制BeanPostProcessor前端控制器模式Web MVCDispatcherServlet注册表模式Bean定义管理BeanDefinitionRegistry学习Spring,也是在学习如何优雅地应用设计模式。
并发安全 - ConcurrentHashMap的选择
Spring容器是线程安全的,SpringWind也要保证这一点。我们使用ConcurrentHashMap而不是Hashtable或加锁的HashMap:- // ✓ 推荐:ConcurrentHashMapprivate Map singletonObjects = new ConcurrentHashMap();// ✗ 不推荐:Hashtable(方法级锁,性能差)private Map singletonObjects = new Hashtable();// ✗ 不推荐:synchronized包装(同样是粗粒度锁)private Map singletonObjects =<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />Collections.synchronizedMap(new HashMap());
复制代码 ConcurrentHashMap使用分段锁,并发性能远高于Hashtable。
参照Spring框架设计Springwind的架构
整体架构设计
SpringWind采用分层架构,从下到上分为四层:核心模块设计
1. IoC容器模块
核心类: SpringWindApplicationContext
主要职责:
- 组件扫描和注册
- Bean的创建和缓存
- 依赖注入
- 生命周期管理
- 循环依赖解决(三级缓存)
关键代码(SpringWindApplicationContext.java):- public class SpringWindApplicationContext {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// Bean定义映射表(使用ConcurrentHashMap提升并发性能)<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />private final Map beanDefinitionMap = new ConcurrentHashMap();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 三级缓存机制(解决循环依赖的核心)<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 一级缓存:完整初始化的Bean对象(依赖注入完成、初始化方法执行完成)<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />private final Map singletonObjects = new ConcurrentHashMap();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 二级缓存:早期Bean引用(已实例化但未完成依赖注入,可能是代理对象)<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />private final Map earlySingletonObjects = new ConcurrentHashMap();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 三级缓存:ObjectFactory工厂对象(关键!延迟生成早期引用,支持AOP提前代理)<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />private final Map, Constructor> constructorCache = new ConcurrentHashMap();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public SpringWindApplicationContext(Class configClass) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />scanComponents(configClass);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /> // 1. 扫描组件<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />createSingletonBeans();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /> // 2. 创建单例Bean<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />dependencyInjection();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 3. 依赖注入<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />invokeInitMethods();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /> // 4. 执行初始化方法(已在getBean中完成,保留兼容)<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}}
复制代码 Bean创建流程核心代码(SpringWindApplicationContext.java的getBean方法):关键点:
- 步骤5:放入的是ObjectFactory,而不是Bean本身,这是三级缓存的精髓
- 步骤6:依赖注入时可能触发循环依赖,会走步骤2的逻辑
- 步骤9:如果在getEarlyBeanReference中已创建代理,这里会检测并跳过
2. AOP模块
核心类: AspectProcessor、SmartInstantiationAwareBeanPostProcessor、AopInvocationHandler、AopMethodInterceptor
主要职责:
- 切面注册和管理
- 代理对象创建(JDK动态代理/CGLIB字节码增强)
- 切点匹配和通知执行
- 早期代理创建(通过SmartInstantiationAwareBeanPostProcessor解决循环依赖+AOP的组合问题)
关键设计:AspectProcessor实现SmartInstantiationAwareBeanPostProcessor接口
这是解决"循环依赖+AOP"场景的核心设计:工作原理:
- 正常场景(无循环依赖):
- getEarlyBeanReference不会被调用
- 在postProcessAfterInitialization中创建AOP代理
- 循环依赖场景:
- 当Bean A被Bean B依赖时,会触发ObjectFactory.getObject()
- getObject()内部调用getEarlyBeanReference(beanA)
- 在getEarlyBeanReference中提前创建AOP代理
- 代理对象被放入二级缓存,B获取到的是代理
- 后续postProcessAfterInitialization检测到已创建代理,跳过
为什么需要这个机制?
- 如果不在getEarlyBeanReference中创建代理,B获取到的是原始对象
- 后续在postProcessAfterInitialization创建代理后,A持有的是代理对象,但B持有的仍是原始对象
- 导致同一个Bean存在两个不同的对象,违反了单例原则
通过SmartInstantiationAwareBeanPostProcessor,确保:
- 循环依赖的所有Bean获取到同一个代理对象
- 不会产生多个不同的代理或原始对象
代理创建逻辑(AspectProcessor.java):- public Object createProxy(Object target) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />List matchedAspects = findMatchedAspects(target.getClass());<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />if (matchedAspects.isEmpty()) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return target; // 没有切面,返回原始对象<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 如果实现了接口,使用JDK动态代理<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />Class[] interfaces = target.getClass().getInterfaces();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />if (interfaces.length > 0) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return Proxy.newProxyInstance(<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />target.getClass().getClassLoader(),<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />interfaces,<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />new AopInvocationHandler(target, matchedAspects)<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 否则使用CGLIB代理<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />Enhancer enhancer = new Enhancer();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />enhancer.setSuperclass(target.getClass());<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />enhancer.setCallback(new AopMethodInterceptor(target, matchedAspects));<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return enhancer.create();}
复制代码 3. Web MVC模块
核心类: DispatcherServlet、HandlerMapping、PathMatcher
主要职责:
- 请求路由
- 参数解析和绑定
- Controller调用
- 视图解析
请求处理流程:
---title: Spring MVC 请求处理流程---flowchart LR<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />A["HTTP请求"] --> B["DispatcherServlet
前端控制器"]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />B --> C["HandlerMapping
处理器映射"]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />C --> D[" athMatcher
路径匹配器"]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />D --> E["参数解析器
@PathVariable
@RequestParam
@RequestBody"]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />E --> F["Controller方法
业务逻辑处理"]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />F --> G["结果处理器
JSON/视图转换"]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />G --> H["HTTP响应"]<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" /><bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />%% 样式定义<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style A fill:#e3f2fd,stroke:#1565c0,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style B fill:#e8f5e8,stroke:#388e3c,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style C fill:#fff3e0,stroke:#f57c00,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style D fill:#fce4ec,stroke:#c2185b,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style E fill:#e1f5fe,stroke:#01579b,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style F fill:#fff8e1,stroke:#ffb300,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style G fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px<bean id="userService" >
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" />style H fill:#e8f5e8,stroke:#388e3c,stroke-width:2px4. JDBC模块
核心类: JdbcTemplate、RowMapper
主要职责:
模板方法实现:- public List query(String sql, RowMapper mapper, Object... args) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />Connection conn = null;<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />PreparedStatement stmt = null;<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />ResultSet rs = null;<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />try {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />conn = getConnection();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 固定步骤1<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />stmt = prepareStatement(sql); // 固定步骤2<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />setParameters(stmt, args);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /> // 固定步骤3<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />rs = stmt.executeQuery();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /> // 固定步骤4<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return mapResults(rs, mapper); // 可变步骤(回调)<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />} catch (SQLException e) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />throw new RuntimeException(e);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />} finally {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />closeResources(rs, stmt, conn); // 固定步骤5<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}}
复制代码 注解体系设计
SpringWind定义了一套完整的注解体系(共21个注解):
IoC/DI 注解:
注解作用元注解@Component通用组件-@Service服务层组件@Component@Controller控制器组件@Component@Repository数据访问层组件@Component@Configuration配置类@Component@Autowired依赖注入-@Bean方法级Bean定义-生命周期注解:
注解作用来源@PostConstructBean 初始化方法javax.annotation@PreDestroyBean 销毁方法javax.annotationWeb MVC 注解:
注解作用说明@RequestMappingURL映射(通用)支持所有HTTP方法@GetMappingGET请求映射快捷注解@PostMappingPOST请求映射快捷注解@PutMappingPUT请求映射快捷注解@DeleteMappingDELETE请求映射快捷注解@PathVariable路径变量绑定如 /user/{id}@RequestParam请求参数绑定如 ?name=xxx@RequestBody请求体绑定(JSON)自动反序列化@ResponseBody响应体(JSON)自动序列化AOP 注解:
注解作用通知类型@Aspect标记切面类-@Before前置通知方法执行前@After后置通知方法执行后@Around环绕通知完全控制方法执行事务注解:
注解作用状态@Transactional声明式事务需配合AOP实现性能优化策略
1. 构造器缓存
反射获取构造器的成本较高,缓存起来:- private final Map> constructorCache = new ConcurrentHashMap();public Object createBean(Class clazz) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />Constructor constructor = constructorCache.computeIfAbsent(clazz, c -> {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />try {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return c.getDeclaredConstructor();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />} catch (NoSuchMethodException e) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />throw new BeanCreationException("找不到无参构造器", e);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />});<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return constructor.newInstance();}
复制代码 2. 切点表达式缓存
正则表达式编译很慢,缓存编译后的Pattern:- private final Map patternCache = new ConcurrentHashMap();public boolean matches(String pointcut, String methodName) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />Pattern pattern = patternCache.computeIfAbsent(pointcut, p -> {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />String regex = pointcut.replace("*", ".*").replace(".", "\\.");<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return Pattern.compile(regex);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />});<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return pattern.matcher(methodName).matches();}
复制代码 3. 使用ConcurrentHashMap
所有需要并发访问的Map都使用ConcurrentHashMap,避免锁竞争。
SpringWind 与 Spring MVC 的对比
了解 SpringWind 与 Spring MVC 的异同,可以帮助你更好地理解框架的设计取舍。
功能对比表
功能特性SpringWindSpring MVC说明IoC 容器✅ 基本实现✅ 完整实现 + 复杂场景SpringWind 实现了单例 Bean 管理、依赖注入、生命周期管理依赖注入✅ 字段注入、方法注入✅ 字段、方法、构造器注入SpringWind 暂不支持构造器注入循环依赖解决✅ 三级缓存 + SmartInstantiationAwareBeanPostProcessor✅ 三级缓存 + 多种策略SpringWind 核心机制与 Spring 相同,都支持早期代理创建BeanPostProcessor✅ 基本实现✅ 完整实现 + 多种类型SpringWind 支持前置和后置处理SmartInstantiation
AwareBeanPostProcessor✅ 支持✅ 完整支持SpringWind 支持在 Bean 实例化早期创建代理,解决循环依赖Bean 作用域⚠️ 仅单例(Singleton)✅ Singleton、Prototype、Request、Session 等SpringWind 只支持单例模式AOP 支持✅ JDK/CGLIB 代理 + 早期代理✅ AspectJ 集成 + 更强大的切点表达式SpringWind 实现了基本的 AOP 功能,支持早期代理创建切面类型✅ @Before、@After、@Around✅ @Before、@After、@Around、@AfterReturning、@AfterThrowingSpringWind 支持三种基本通知类型Web MVC✅ 基本实现✅ 完整的 MVC 栈 + 异步支持SpringWind 实现了核心的请求映射和参数绑定请求映射✅ @RequestMapping✅ @RequestMapping、@GetMapping、@PostMapping 等SpringWind 使用统一的 @RequestMapping参数绑定✅ @PathVariable、@RequestParam、@RequestBody✅ 更多参数类型和转换器SpringWind 支持基本的参数绑定JSON 响应✅ 自动序列化✅ 多种视图解析器SpringWind 使用 Jackson 进行 JSON 序列化JDBC 支持✅ JdbcTemplate✅ JdbcTemplate + JPA/Hibernate 集成SpringWind 实现了 JdbcTemplate 的核心功能生命周期回调✅ @PostConstruct、@PreDestroy✅ @PostConstruct、@PreDestroy、InitializingBean、DisposableBeanSpringWind 支持标准的生命周期回调事务管理❌ 需自行实现✅ 声明式和编程式事务SpringWind 暂不支持,可通过 AOP 自己实现配置方式✅ 注解驱动✅ 注解 + XML + Java ConfigSpringWind 主要使用注解配置国际化(i18n)❌ 不支持✅ MessageSourceSpring 提供完整的国际化支持事件机制❌ 不支持✅ ApplicationEventSpring 提供事件发布和监听机制SpEL 表达式❌ 不支持✅ 完整的表达式语言Spring 支持强大的 SpEL 表达式代码量~5000 行~50 万行SpringWind 代码量相对较小,易于理解学习难度⭐⭐ 容易⭐⭐⭐⭐ 较难SpringWind 适合初学者理解原理生产就绪✅ 教学/小项目✅ 企业级应用Spring 经过大规模生产验证使用场景对比
场景推荐使用原因学习 Spring 原理SpringWind代码简洁,易于理解核心机制企业级应用Spring MVC功能完整,生产就绪小型工具项目SpringWind轻量级,快速启动Demo/原型开发SpringWind简单直接,无需复杂配置大型分布式系统Spring Boot/Cloud完整的生态系统和中间件支持需要事务支持Spring MVCSpringWind 需要自己实现事务需要复杂 AOPSpring MVC支持 AspectJ 和复杂切点表达式快速原型验证SpringWind启动快,依赖少应用代码对比示例
定义一个简单的 Controller:- // SpringWind 和 Spring MVC 的代码几乎完全相同@Controller@RequestMapping("/user")public class UserController {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />@Autowired<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />private UserService userService;<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />@RequestMapping("/list")<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />@ResponseBody<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public List list() {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return userService.getAllUsers();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />@RequestMapping("/get")<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />@ResponseBody<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public User getUser(@RequestParam("id") Long id) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return userService.getUserById(id);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />@RequestMapping("/create")<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />@ResponseBody<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public User createUser(@RequestBody User user) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return userService.createUser(user);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}}
复制代码 启动应用:- // SpringWindpublic class Application {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public static void main(String[] args) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 启动SpringWind应用<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />SpringWindApplication app = SpringWindApplication.run(WebDemoApplication.class, args);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 启动 Tomcat<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />TomcatServer.start(app.getContext());<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}}// Spring Boot@SpringBootApplicationpublic class Application {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public static void main(String[] args) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />SpringApplication.run(Application.class, args);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}}
复制代码 迁移成本
从 SpringWind 迁移到 Spring MVC:
- 难度:⭐⭐ 容易
- 工作量:主要是替换依赖和配置
- 代码改动:业务代码几乎不需要改动
- 注意事项:
- 添加 Spring Boot Starter 依赖
- 调整配置文件(application.yml/properties)
- 构造器注入需要添加构造器
- 事务注解需要启用 @EnableTransactionManagement
性能对比
指标SpringWindSpring MVC启动时间~200ms~2-5s(Spring Boot)内存占用~50MB~150-300MB依赖数量~5 个~50-100 个jar 包大小~2MB~20-50MB并发性能良好优秀注意:SpringWind 的性能优势主要体现在轻量级场景,没有深入优化高并发,对于大型应用,Spring 的优化更加全面。
学习建议
- 先学 SpringWind,再学 Spring
- SpringWind 代码量小,容易看懂核心原理
- 理解了原理后,再看 Spring 源码会轻松很多
- 对比学习
- 用 SpringWind 写一个小项目
- 用 Spring Boot 实现同样的功能
- 对比两者的实现差异
- 渐进式学习
- IoC/DI → AOP → MVC → JDBC → 事务
- 每个模块都先在 SpringWind 中理解,再看 Spring 的实现
Springwind框架有什么用
SpringWind不是为了替代Spring,而是为了学习Spring,掌握Spring。但它也有自己的价值:
1. 彻底搞懂Spring原理和运行机制
面试常问的问题,SpringWind都有答案:
- IoC容器是怎么工作的?
看SpringWindApplicationContext的实现,200多行代码就能看懂。
- 循环依赖怎么解决的?
看三级缓存的实现,代码简洁明了。
- AOP是怎么实现的?
看AspectProcessor如何创建代理对象。
- @Autowired是怎么注入的?
看dependencyInjection方法,反射设置字段值。
- DispatcherServlet是怎么分发请求的?
看Web MVC模块,前端控制器模式的经典实现。
学习建议:
- 先跑一遍示例项目,看看效果
- 打断点调试,看Bean是怎么创建的
- 对照Spring源码,理解设计思想
- 自己动手改代码,加功能
2. 用在小项目上快速开发
Spring确实有点重,一个简单的Web项目,引入Spring Boot全家桶可能有上百个依赖。
SpringWind只有几个核心依赖:- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />jakarta.servlet<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />jakarta.servlet-api<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />cglib<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />cglib<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />org.slf4j<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />slf4j-api<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />
复制代码 适用场景:
- 小型Web应用
- 内部工具系统
- 学习项目
- Demo演示
3. 探索新功能,给Spring提建议
SpringWind是一个试验田,你可以:
- 尝试新的设计思路
- 验证性能优化方案
- 实现Spring没有的功能
- 给Spring社区提PR
比如,你可以尝试:
- 实现响应式编程支持
- 优化Bean创建性能
- 简化配置方式
- 增强AOP功能
Springwind的项目结构
SpringWind的代码组织清晰,模块划分明确:核心模块说明:
模块包名核心类行数说明注解annotation20个注解类~350定义框架的注解体系IoC容器coreSpringWindApplicationContext~730容器核心,Bean管理,三级缓存AOPaopAspectProcessor~150切面处理,动态代理创建Web MVCwebDispatcherServlet~660请求分发,参数绑定,视图解析JDBCjdbcJdbcTemplate~260数据库操作模板,结果映射异常exception4个异常类~110框架异常定义工具utilClassScanner、StringUtils~420类扫描、字符串处理整个框架核心代码约 3800 行,但实现了Spring的核心功能,代码精简而功能完整。
Springwind使用实践
光说不练假把式,下面我们通过两个实际示例来看看SpringWind怎么用。
示例1:User Demo - 完整的用户管理系统
这是一个基于数据库的用户管理系统,展示了SpringWind的IoC、DI、Web MVC和JDBC功能。请从examples里面找到user-demo目录。
项目结构
典型的三层架构:Controller → Service → Dao- user-demo/├── controller/│ └── UserController.java<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /> # 处理HTTP请求├── service/│ └── UserService.java<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /> # 业务逻辑├── dao/│ └── UserDao.java<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /> # 数据访问(使用JdbcTemplate)├── model/│ └── User.java<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /> # 实体类├── config/│ └── DataSourceConfig.java<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /># 数据源配置└── utils/<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />├── ApiResponse.java<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /> # 统一响应封装<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />└── PageResult.java<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /> # 分页结果
复制代码 1. 定义实体类
- public class User {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />private Long id;<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />private String name;<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />private String email;<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />private String phone;<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />private Long createdTime;<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />private Long updatedTime;<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// getter/setter省略}
复制代码 2. 数据访问层 - 使用JdbcTemplate
- @Repositorypublic class UserDao {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />@Autowired<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />private JdbcTemplate jdbcTemplate;<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 查询所有用户<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public List findAll() {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return jdbcTemplate.query(<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />"SELECT * FROM users ORDER BY id DESC",<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />this::mapRow<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 根据ID查询<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public User findById(Long id) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return jdbcTemplate.queryForObject(<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />"SELECT * FROM users WHERE id = ?",<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />this::mapRow,<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />id<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 分页查询<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public List findByPage(int page, int pageSize) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />int offset = (page - 1) * pageSize;<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return jdbcTemplate.query(<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />"SELECT * FROM users ORDER BY id DESC LIMIT ? OFFSET ?",<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />this::mapRow,<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />pageSize, offset<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 创建用户<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public int insert(User user) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return jdbcTemplate.update(<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />"INSERT INTO users (name, email, phone, created_time, updated_time) " +<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />"VALUES (?, ?, ?, ?, ?)",<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />user.getName(), user.getEmail(), user.getPhone(),<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />System.currentTimeMillis(), System.currentTimeMillis()<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 更新用户<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public int update(User user) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return jdbcTemplate.update(<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />"UPDATE users SET name = ?, email = ?, phone = ?, " +<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />"updated_time = ? WHERE id = ?",<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />user.getName(), user.getEmail(), user.getPhone(),<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />System.currentTimeMillis(), user.getId()<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 删除用户<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public int delete(Long id) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return jdbcTemplate.update("DELETE FROM users WHERE id = ?", id);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 结果集映射<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />private User mapRow(ResultSet rs, int rowNum) throws SQLException {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />User user = new User();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />user.setId(rs.getLong("id"));<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />user.setName(rs.getString("name"));<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />user.setEmail(rs.getString("email"));<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />user.setPhone(rs.getString("phone"));<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />user.setCreatedTime(rs.getLong("created_time"));<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />user.setUpdatedTime(rs.getLong("updated_time"));<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return user;<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}}
复制代码 看到了吗? 原本50多行的JDBC代码,现在只需要2行。JdbcTemplate自动处理了连接管理、资源关闭、异常处理。
3. 服务层 - 业务逻辑
- @Servicepublic class UserService {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />@Autowired<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />private UserDao userDao;<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public List getAllUsers() {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return userDao.findAll();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public User getUserById(Long id) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return userDao.findById(id);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public List getUsersByPage(int page, int pageSize) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return userDao.findByPage(page, pageSize);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public Long getUserCount() {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return userDao.count();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public boolean createUser(User user) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return userDao.insert(user) > 0;<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public boolean updateUser(User user) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return userDao.update(user) > 0;<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public boolean deleteUser(Long id) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return userDao.delete(id) > 0;<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}}
复制代码 服务层很薄,主要是调用Dao层。如果有复杂的业务逻辑(比如用户注册需要发送邮件),就写在这里。
4. 控制器层 - RESTful API
5. 统一响应封装 - ApiResponse
为了简化Controller代码,我们提供了ApiResponse工具类:- public class ApiResponse {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />private Integer code;<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />private String message;<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />private Object data;<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 成功响应<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public static ApiResponse success(Object data, String message) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return new ApiResponse(200, message, data);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 分页响应<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public static ApiResponse page(List list, int page, int pageSize, long total) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />PageResult pageResult = PageResult.of(list, page, pageSize, total);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return success(pageResult, "获取列表成功");<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 失败响应<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public static ApiResponse failure(String message) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return new ApiResponse(500, message, null);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public static ApiResponse badRequest(String message) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return new ApiResponse(400, message, null);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public static ApiResponse notFound(String message) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return new ApiResponse(404, message, null);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}}
复制代码 对比一下:
传统方式(手动构建Map):- public class User {
- private Long id;
- private String name;
- private String email;
- private String phone;
- private Long createdTime;
- private Long updatedTime;
- // getter/setter省略
- }
复制代码 使用ApiResponse(一行搞定):- @Repository
- public class UserDao {
- @Autowired
- private JdbcTemplate jdbcTemplate;
- // 查询所有用户
- public List<User> findAll() {
- return jdbcTemplate.query(
- "SELECT * FROM users ORDER BY id DESC",
- this::mapRow
- );
- }
- // 根据ID查询
- public User findById(Long id) {
- return jdbcTemplate.queryForObject(
- "SELECT * FROM users WHERE id = ?",
- this::mapRow,
- id
- );
- }
- // 分页查询
- public List<User> findByPage(int page, int pageSize) {
- int offset = (page - 1) * pageSize;
- return jdbcTemplate.query(
- "SELECT * FROM users ORDER BY id DESC LIMIT ? OFFSET ?",
- this::mapRow,
- pageSize, offset
- );
- }
- // 创建用户
- public int insert(User user) {
- return jdbcTemplate.update(
- "INSERT INTO users (name, email, phone, created_time, updated_time) " +
- "VALUES (?, ?, ?, ?, ?)",
- user.getName(), user.getEmail(), user.getPhone(),
- System.currentTimeMillis(), System.currentTimeMillis()
- );
- }
- // 更新用户
- public int update(User user) {
- return jdbcTemplate.update(
- "UPDATE users SET name = ?, email = ?, phone = ?, " +
- "updated_time = ? WHERE id = ?",
- user.getName(), user.getEmail(), user.getPhone(),
- System.currentTimeMillis(), user.getId()
- );
- }
- // 删除用户
- public int delete(Long id) {
- return jdbcTemplate.update("DELETE FROM users WHERE id = ?", id);
- }
- // 结果集映射
- private User mapRow(ResultSet rs, int rowNum) throws SQLException {
- User user = new User();
- user.setId(rs.getLong("id"));
- user.setName(rs.getString("name"));
- user.setEmail(rs.getString("email"));
- user.setPhone(rs.getString("phone"));
- user.setCreatedTime(rs.getLong("created_time"));
- user.setUpdatedTime(rs.getLong("updated_time"));
- return user;
- }
- }
复制代码 6. 分页结果 - PageResult
分页是Web应用的常见需求,SpringWind提供了PageResult:分页响应示例:- { "code": 200, "message": "获取列表成功", "data": {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />"list": [<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /> {"id": 1, "name": "admin", "email": "admin@example.com"},<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /> {"id": 2, "name": "test", "email": "test@example.com"}<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />],<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />"page": 2,<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />"pageSize": 10,<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />"total": 100,<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />"totalPages": 10,<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />"hasPrevious": true,<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />"hasNext": true,<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />"first": false,<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />"last": false }}
复制代码 前端可以根据hasPrevious和hasNext来禁用/启用分页按钮,非常方便。
7. 启动应用
8. API测试
- Map<String, Object> result = new HashMap<>();
- result.put("code", 200);
- result.put("message", "获取用户成功");
- result.put("data", user);
- return new JsonResult(result);
复制代码 示例2:Web Demo - 企业网站内容管理系统
这是一个企业网站的CMS系统,展示了SpringWind的Web MVC功能。请见examples/web-demo,此处不再展开。
Springwind改进点
SpringWind目前实现了Spring的核心功能,但还有很多可以改进的地方。
已实现的功能 ✅
- IoC容器(组件扫描、Bean管理)
- 依赖注入(字段注入、方法注入)
- 循环依赖解决(三级缓存)
- AOP(JDK动态代理、CGLIB代理)
- Web MVC(请求映射、参数绑定、JSON响应)
- JDBC模板(查询、更新、批量操作)
- 分页支持(PageResult)
- 统一响应封装(ApiResponse)
待改进的功能 ⏳
1. 事务管理
当前状态: 没有事务支持,需要手动管理
改进方案:2. 构造器注入
当前状态: 只支持字段注入和方法注入
改进方案:- // 支持构造器注入@Servicepublic class UserService {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />private final UserDao userDao; // final保证不可变<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />@Autowired<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public UserService(UserDao userDao) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />this.userDao = userDao;<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}}// 实现思路:// 1. 扫描构造器上的@Autowired// 2. 解析构造器参数类型// 3. 从容器中查找对应的Bean// 4. 调用构造器创建对象
复制代码 3. Bean作用域
当前状态: 只支持单例(Singleton)
改进方案:- @Service@Scope("prototype") // 每次获取都创建新实例public class PrototypeService {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// ...}@Service@Scope("request") // 每个HTTP请求一个实例public class RequestScopedService {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// ...}
复制代码 4. 更强大的AOP
当前改进:
- @Before("execution(* com.example.service.*.*(..))") // 支持AspectJ表达式public void logBefore(JoinPoint joinPoint) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// ...}
复制代码- @Pointcut("execution(* com.example.service.*.*(..))")public void serviceMethods() {}@Pointcut("@annotation(Transactional)")public void transactionalMethods() {}@Before("serviceMethods() && transactionalMethods()")public void beforeServiceTransaction() {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />// 同时满足两个切点}
复制代码- @Around("com.example.service.*.*")public Object aroundAdvice(ProceedingJoinPoint pjp) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />Object[] args = pjp.getArgs(); // 获取方法参数<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />System.out.println("参数: " + Arrays.toString(args));<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return pjp.proceed(); // 继续执行}
复制代码 5. 更轻量级化
当前体积: 核心代码约2000行,依赖较少
进一步优化:
- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />com.github.microwind<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />springwind-core<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /> com.github.microwind<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />springwind-web
复制代码- <bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />cglib<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />cglib<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />true
复制代码
- GraalVM Native Image支持:
编译成原生可执行文件,启动速度更快,内存占用更小。
6. 配置文件支持
当前状态: 主要依赖注解
改进方案:- # application.ymlspringwind: datasource:<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />url: jdbc:mysql://localhost:3306/test<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />username: root<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />password: 123456 web:<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />port: 8080<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />context-path: /api
复制代码- // 读取配置@Configurationpublic class DataSourceConfig {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />@Value("${springwind.datasource.url}")<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />private String url;<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />@Bean<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public DataSource dataSource() {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return new HikariDataSource(url, username, password);<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}}
复制代码 7. 更好的异常处理
当前状态: 基本的异常抛出
改进方案:- // 全局异常处理器@ControllerAdvicepublic class GlobalExceptionHandler {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />@ExceptionHandler(BeanNotFoundException.class)<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public ViewResult handleBeanNotFound(BeanNotFoundException e) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return new JsonResult(ApiResponse.notFound(e.getMessage()).toMap());<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />@ExceptionHandler(Exception.class)<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public ViewResult handleGenericException(Exception e) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return new JsonResult(ApiResponse.failure("系统错误: " + e.getMessage()).toMap());<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}}
复制代码 8. 性能监控和诊断
改进方案:- // Bean创建性能监控@Aspectpublic class BeanCreationMonitor {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />@Around("@annotation(Component)")<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public Object monitorBeanCreation(ProceedingJoinPoint pjp) {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />long start = System.currentTimeMillis();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />Object bean = pjp.proceed();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />long end = System.currentTimeMillis();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />System.out.println("创建Bean " + bean.getClass().getSimpleName() +<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /> " 耗时: " + (end - start) + "ms");<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />return bean;<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}}// 内存占用监控public class MemoryMonitor {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />public void printMemoryUsage() {<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />Runtime runtime = Runtime.getRuntime();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />long usedMemory = runtime.totalMemory() - runtime.freeMemory();<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" /><bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />System.out.println("内存占用: " + (usedMemory / 1024 / 1024) + " MB");<bean id="userService" >
- <property name="userDao" ref="userDao"/>
- </bean>
- <bean id="userDao" />}}
复制代码 总结
通过从零实现SpringWind框架(~5000行代码),我们彻底搞懂了Spring的核心原理:
核心机制理解
- IoC容器:通过反射扫描组件、注册Bean定义、管理完整生命周期
- 组件扫描:支持文件系统和JAR包中的类扫描
- Bean注册:使用ConcurrentHashMap保证线程安全
- 生命周期:从实例化到初始化到销毁的完整流程
- 依赖注入:通过反射实现自动装配,支持字段注入和方法注入
- 类型匹配:根据字段类型自动查找Bean
- 循环依赖:与三级缓存配合,优雅解决循环依赖问题
- 三级缓存(最精妙的设计):
- 一级缓存:存储完全初始化好的Bean
- 二级缓存:存储早期Bean引用(已实例化但未初始化)
- 三级缓存:存储ObjectFactory工厂(关键!)
- 为什么需要三级:延迟生成早期引用,支持AOP提前创建代理,保证单例一致性
- AOP:通过动态代理实现横切关注点的分离
- JDK动态代理:基于接口的代理
- CGLIB代理:基于继承的字节码增强
- SmartInstantiationAwareBeanPostProcessor:解决循环依赖+AOP的组合问题
- Web MVC:通过前端控制器模式统一处理HTTP请求
- DispatcherServlet:统一入口
- HandlerMapping:URL到方法的映射
- 参数解析:支持@PathVariable、@RequestParam、@RequestBody
- JDBC模板:通过模板方法模式封装样板代码
- 固定流程:连接管理、语句执行、结果映射、资源关闭
- 灵活扩展:通过RowMapper实现不同的映射策略
学习价值
为了就是模仿,只有通过模仿完全手搓一个Web框架时,你才能彻底明白框架的核心机制。
SpringWind不是为了替代Spring,而是为了深入理解Spring。它用最简洁的代码(~3800行)实现了Spring的核心功能,让每个想深入理解框架原理的开发者都能看懂。
通过学习SpringWind,你将掌握Spring框架核心知识点:
- ✅ Spring IoC容器的工作机制
- ✅ 三级缓存解决循环依赖的精妙设计
- ✅ AOP动态代理的实现原理
- ✅ Bean生命周期的完整流程
- ✅ 设计模式在框架中的实际应用
- ✅ 为什么Spring要这样设计的深层原因
学习建议:
- 先跑起来:运行两个示例项目,看看效果
- 打断点调试:看Bean是怎么创建的,依赖是怎么注入的
- 阅读源码:SpringWind代码量不大,完全可以通读
- 对比Spring:理解SpringWind的简化设计和Spring的完整实现
- 动手改代码:实现待改进功能,加深理解
最后的话:
框架不是黑魔法,它只是把常见的代码模式封装起来。当你理解了原理,就能从"会用框架"进阶到"精通框架",甚至"设计框架"。
希望SpringWind能帮你打开Spring的大门,祝你学习愉快!
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |