1. 框架生态全景图:从Spring到MyBatis的技术栈演进
第一次接触Spring框架是在2010年,当时为了替换公司老旧的Struts项目。记得在xml配置里写bean定义写到怀疑人生,一个简单的依赖注入要配十几行xml。如今这些框架已经发展成完整的生态体系,但很多开发者对这些框架的定位和关系仍然存在困惑。
Spring是整个生态的基石,相当于Java EE的增强版。Spring MVC是基于Spring的Web框架,而Spring Boot是Spring的快速开发脚手架。Spring Cloud则是基于Spring Boot的微服务解决方案集。MyBatis作为持久层框架,通常与Spring家族配合使用。这就像建造房子:Spring是地基,Spring MVC是房屋框架,Spring Boot是预制构件,Spring Cloud是小区规划,MyBatis则是水电管线系统。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Spring框架核心机制解析
2.1 IOC容器与三级缓存原理
Spring最核心的特性是IOC(控制反转)容器。想象你去餐厅点菜,传统方式是直接到后厨让厨师做菜(程序主动创建对象),而IOC就像在餐桌点单,由服务员(容器)决定何时让厨师做菜。这个机制通过三级缓存实现:
- 一级缓存(singletonObjects):存放完全初始化好的单例Bean
- 二级缓存(earlySingletonObjects):存放提前暴露的原始Bean(解决循环依赖)
- 三级缓存(singletonFactories):存放Bean工厂对象
java复制// 典型的三级缓存访问逻辑
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
return singletonObject;
}
关键点:三级缓存主要解决循环依赖问题。比如A依赖B,B又依赖A时,Spring通过提前暴露对象引用避免死锁
2.2 AOP实现机制
AOP(面向切面编程)是Spring另一大核心功能。其底层使用动态代理技术,分为JDK动态代理和CGLIB两种方式:
| 代理类型 | 条件 | 性能 | 限制 |
|---|---|---|---|
| JDK动态代理 | 目标类实现接口 | 调用快,创建慢 | 只能代理接口方法 |
| CGLIB | 目标类未实现接口 | 创建快,调用稍慢 | 不能代理final方法 |
实际项目中,声明式事务(@Transactional)就是典型AOP应用。我曾遇到一个坑:在同一个类中方法A调用方法B,B上的事务注解会失效,这是因为自调用不走代理。
3. Spring MVC的工作机制
3.1 请求处理流程
Spring MVC的核心是DispatcherServlet,处理流程如下:
- 用户发送请求 → DispatcherServlet接收
- HandlerMapping解析请求URL → 找到对应Controller
- HandlerAdapter执行Controller方法
- 返回ModelAndView → 视图解析器处理
- 渲染视图 → 返回响应
java复制// 典型Controller示例
@Controller
@RequestMapping("/users")
public class UserController {
@GetMapping("/{id}")
public String getUser(@PathVariable Long id, Model model) {
User user = userService.findById(id);
model.addAttribute("user", user);
return "userDetail";
}
}
3.2 参数绑定原理
Spring MVC的参数绑定非常灵活:
- @RequestParam:绑定查询参数
- @PathVariable:绑定URL路径变量
- @RequestBody:绑定JSON请求体
- @ModelAttribute:绑定表单数据
曾遇到一个日期格式化问题:前端传"2023-01-01",后端用LocalDate接收报错。解决方案是在配置类中添加:
java复制@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
registrar.setUseIsoFormat(true);
registrar.registerFormatters(registry);
}
}
4. Spring Boot的自动装配魔法
4.1 自动配置原理
Spring Boot的核心是@SpringBootApplication注解,它包含三个关键元注解:
- @SpringBootConfiguration:标识这是配置类
- @EnableAutoConfiguration:启用自动配置
- @ComponentScan:开启组件扫描
自动配置的秘密在spring.factories文件中。比如DataSource自动配置:
properties复制# META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
当classpath存在DataSource类时,相关配置自动生效。这种"约定优于配置"的理念极大简化了开发。
4.2 启动过程详解
Spring Boot启动分为几个阶段:
- 创建SpringApplication实例
- 运行run()方法
- 准备Environment
- 创建ApplicationContext
- 执行ApplicationContextInitializer
- 加载Bean定义
- 发布ApplicationStartedEvent
- 调用CommandLineRunner
我曾优化过一个启动慢的项目,发现是@ComponentScan扫描范围太大。解决方案是指定精确扫描路径:
java复制@SpringBootApplication(scanBasePackages = "com.myapp")
5. Spring Cloud微服务架构
5.1 核心组件协同
Spring Cloud的典型架构包含:
- 服务注册中心(Eureka/Nacos)
- 配置中心(Config/Nacos)
- 服务网关(Gateway/Zuul)
- 熔断器(Hystrix/Sentinel)
- 负载均衡(Ribbon/LoadBalancer)
yaml复制# 网关路由配置示例
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/users/**
5.2 服务调用方式
微服务间通信主要有两种方式:
- RestTemplate + @LoadBalanced
java复制@Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } - OpenFeign声明式客户端
java复制@FeignClient(name = "order-service") public interface OrderClient { @GetMapping("/orders") List<Order> getOrders(@RequestParam Long userId); }
注意点:Feign默认超时是1秒,生产环境需要调整:
properties复制feign.client.config.default.connectTimeout=5000
feign.client.config.default.readTimeout=5000
6. MyBatis持久层实战
6.1 核心工作原理
MyBatis的核心组件:
- SqlSessionFactory:创建SqlSession
- SqlSession:执行CRUD操作
- Mapper接口:映射SQL语句
- Executor:执行SQL语句
xml复制<!-- 典型mapper.xml -->
<mapper namespace="com.example.UserMapper">
<select id="selectUser" resultType="User">
SELECT * FROM user WHERE id = #{id}
</select>
</mapper>
6.2 动态SQL技巧
MyBatis强大的动态SQL能力:
xml复制<select id="findUsers" resultType="User">
SELECT * FROM user
<where>
<if test="name != null">
AND name LIKE #{name}
</if>
<if test="status != null">
AND status = #{status}
</if>
</where>
ORDER BY id
</select>
分页查询推荐使用PageHelper:
java复制PageHelper.startPage(1, 10); // 第1页,每页10条
List<User> users = userMapper.selectAll();
PageInfo<User> pageInfo = new PageInfo<>(users);
7. 框架整合最佳实践
7.1 Spring Boot整合MyBatis
- 添加依赖:
xml复制<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
- 配置数据源:
properties复制spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=123456
mybatis.mapper-locations=classpath:mapper/*.xml
- 扫描Mapper接口:
java复制@MapperScan("com.example.mapper")
@SpringBootApplication
public class Application { ... }
7.2 事务管理要点
Spring事务的传播行为:
- REQUIRED(默认):当前有事务则加入,没有则新建
- REQUIRES_NEW:总是新建事务
- NESTED:嵌套事务
java复制@Service
public class OrderService {
@Transactional(propagation = Propagation.REQUIRED,
rollbackFor = Exception.class)
public void createOrder(Order order) {
// 业务逻辑
}
}
常见坑点:@Transactional注解在private方法上无效,因为动态代理无法拦截私有方法。
8. 性能优化实战经验
8.1 MyBatis缓存配置
一级缓存(SqlSession级别)默认开启,二级缓存需要手动配置:
xml复制<mapper namespace="com.example.UserMapper">
<cache eviction="LRU" flushInterval="60000" size="512"/>
</mapper>
注意:二级缓存是跨SqlSession的,更新操作多的场景慎用
8.2 Spring Boot性能调优
- JVM参数优化:
bash复制java -Xms512m -Xmx512m -XX:+UseG1GC -jar app.jar
- Tomcat调优:
properties复制server.tomcat.max-threads=200
server.tomcat.accept-count=100
- 启用响应式编程(WebFlux):
java复制@RestController
public class UserController {
@GetMapping("/users")
public Flux<User> getUsers() {
return userReactiveRepository.findAll();
}
}
9. 常见问题排查指南
9.1 启动类冲突
现象:启动报"Unable to start embedded Tomcat"
原因:通常是因为依赖冲突,比如同时引入了spring-boot-starter-web和spring-boot-starter-webflux
解决方案:
bash复制mvn dependency:tree > tree.txt
检查依赖树,排除冲突依赖
9.2 MyBatis映射问题
现象:查询返回null但SQL执行正常
可能原因:
- 属性名与列名不匹配(开启驼峰映射)
properties复制mybatis.configuration.map-underscore-to-camel-case=true - 类型处理器未注册
- resultMap配置错误
9.3 事务不生效场景
- 方法修饰符非public
- 自调用(this.method())
- 异常类型不匹配(默认只回滚RuntimeException)
- 数据库引擎不支持事务(如MyISAM)
10. 技术选型建议
10.1 新项目技术栈选择
| 项目类型 | 推荐组合 | 说明 |
|---|---|---|
| 单体应用 | Spring Boot + MyBatis | 快速开发 |
| 微服务 | Spring Cloud Alibaba | 阿里系组件更符合国内生态 |
| 高并发系统 | Spring WebFlux + R2DBC | 响应式编程 |
| 遗留系统改造 | Spring + JPA | 兼容性好 |
10.2 版本选择原则
- Spring Boot选择GA版本(非SNAPSHOT)
- 次要版本选择最新的稳定版(如3.2.x选3.2.5)
- 注意Java版本兼容性:
- Spring Boot 3.x需要Java 17+
- Spring Boot 2.x支持Java 8+
11. 开发工具链推荐
11.1 IDEA插件
- MyBatisX:Mapper接口与XML跳转
- Spring Assistant:Spring项目支持
- JRebel:热部署
- Grep Console:日志着色
11.2 其他工具
- Postman:API测试
- Jmeter:压力测试
- Arthas:线上诊断
- SkyWalking:链路追踪
12. 学习路线建议
-
基础阶段:
- Spring Core(IOC/AOP)
- Spring MVC流程
- MyBatis CRUD操作
-
进阶阶段:
- Spring Boot自动配置原理
- Spring Cloud组件整合
- MyBatis插件开发
-
高手阶段:
- Spring源码阅读
- 自定义Starter开发
- 分布式事务解决方案
13. 面试常见问题解析
13.1 Spring相关问题
Q:Bean的生命周期?
A:
- 实例化
- 属性填充
- 初始化(@PostConstruct)
- 使用
- 销毁(@PreDestroy)
13.2 MyBatis相关问题
Q:#{}和${}的区别?
A:
- #{}是预编译处理,防止SQL注入
- ${}是字符串替换,适合动态表名等场景
13.3 Spring Boot相关问题
Q:如何自定义Starter?
A:
- 创建autoconfigure模块
- 编写配置类(@Configuration)
- 添加spring.factories
- 创建starter模块依赖autoconfigure
14. 最新技术动态追踪
14.1 Spring 6新特性
- 支持GraalVM原生镜像
- JDK 17+基线要求
- 改进的响应式编程支持
14.2 MyBatis 3.5+
- 新增@MapperScan批量扫描
- 更好的Kotlin支持
- 增强的注解配置
15. 个人实战经验分享
在电商项目中,我们遇到过高并发下单问题。最终解决方案是:
- 使用Spring Cache + Redis缓存商品信息
- 通过@Transactional + for update实现悲观锁
- 用Spring Retry实现重试机制
java复制@Retryable(maxAttempts=3, backoff=@Backoff(delay=100))
@Transactional
public Order createOrder(Long productId, Integer quantity) {
// 查询库存
Product product = productMapper.selectForUpdate(productId);
if (product.getStock() < quantity) {
throw new RuntimeException("库存不足");
}
// 扣减库存
productMapper.updateStock(productId, quantity);
// 创建订单
return orderMapper.insert(new Order(productId, quantity));
}
另一个教训是关于MyBatis批量插入。最初我们使用foreach拼接SQL,当数据量达万级时性能急剧下降。后来改用BatchExecutor:
java复制@Autowired
private SqlSessionFactory sqlSessionFactory;
public void batchInsert(List<User> users) {
try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
UserMapper mapper = session.getMapper(UserMapper.class);
for (User user : users) {
mapper.insert(user);
}
session.commit();
}
}
