Java框架-Spring框架-4
背景
本文是《Java 后端从小白到大神》修仙系列之框架学习,Java框架之Spring框架第四篇
。本篇文章主要聊Java框架
,那么必然从Spring
框架开始,可以说Spring框架是Java企业级开发的基石,若想详细学习请点击首篇博文,我们现在开始吧。
文章概览
- Spring MVC
- 事件与消息(Event & Messaging)
Spring框架
1. Spring MVC
1.1 Spring MVC 是什么
Spring MVC 是基于 Servlet 的 Web 框架,遵循 MVC(Model-View-Controller)设计模式:
- Model:封装业务数据;
- View:负责渲染界面;
- Controller:处理请求、协调 Model 与 View;
这里需要解释一下 Servlet,Servlet 是 Java 提供的一种 处理 Web 请求的技术规范,它是 Java EE 的一部分。一个 Servlet 本质上就是一个 Java 类,部署在 Servlet 容器中(Tomcat、Jetty),用于处理来自浏览器的 HTTP 请求 并生成响应。所谓“Servlet Web 框架”,就是 在 Servlet 技术之上构建的 Web 开发框架,它们提供了 更高级的封装和抽象,例如:Spring MVC、Struts、JSF (JavaServer Faces)。这些框架本质上都运行在 Servlet 容器(如 Tomcat)中,并用 一个 Servlet 入口(前端控制器) 来接管所有请求,进行更灵活的处理。
它的特点有:前端控制器模式(Front Controller)、灵活的 URL 映射、注解驱动、数据绑定与校验、视图解析等。
1.2 整体请求处理流程
以下流程图展示了一个 HTTP 请求在 Spring MVC 中的完整处理链:
graph TD
subgraph 请求处理
direction LR
A(客户端发送 Request) --> B(DispatcherServlet)
B --> C(HandlerMapping)
C --> D(HandlerAdapter >>>)
end
请求处理 --> 响应生成
subgraph 响应生成
direction LR
E(>>> Controller 方法) --> F(返回 ModelAndView)
F --> G(ViewResolver)
G --> H(View 渲染 <br> JSP/Thymeleaf/...)
H --> I(生成 Response 返回客户端)
end
- DispatcherServlet:前端控制器,接收所有请求(在
web.xml
或WebApplicationInitializer
中映射/*
),委派给后续组件。 - HandlerMapping:根据 URL、请求方法等查找对应 Controller 和处理方法。常用实现:
RequestMappingHandlerMapping
。 - HandlerAdapter:负责调用目标 Controller 方法,将
HttpServletRequest
参数转换为方法参数。默认:RequestMappingHandlerAdapter
。 - Controller:使用
@Controller
或@RestController
注解,编写请求处理逻辑,返回ModelAndView
或业务数据。 - ModelAndView:封装视图名称和模型数据(键值对),用于后续渲染。
- ViewResolver:根据视图名称定位实际视图文件(例如 JSP、Thymeleaf 模板),常用实现:
InternalResourceViewResolver
、ThymeleafViewResolver
。 - View:渲染模板,将模型数据填充到视图中,生成最终 HTML。
- Response:由
DispatcherServlet
返回给客户端。
1.3 配置示例
Java 配置 vs XML 配置
|
|
web.xml(前端控制器映射示例)
|
|
web.xml vs web-config.xml 对比总结
对比项 | web.xml(Servlet容器级配置) | web-config.xml(Spring MVC框架级配置) |
---|---|---|
核心作用 | 初始化Servlet容器,注册DispatcherServlet,配置上下文参数、过滤器和监听器 | 配置Spring MVC组件,启用注解支持,设置静态资源处理和数据绑定验证规则 |
配置对象 | Servlet容器(如Tomcat) | Spring MVC框架本身 |
类比(餐厅模型) | 餐厅的"营业执照"和"大门位置" | 餐厅的"菜单"、“服务流程"和"厨房规则” |
关键配置项 | - DispatcherServlet注册 - contextConfigLocation参数 - url-pattern映射 |
- mvc:annotation-driven - component-scan - InternalResourceViewResolver - mvc:resources |
具体示例 | xml<br><servlet><br> <servlet-name>dispatcher</servlet-name><br> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><br> <init-param><br> <param-name>contextConfigLocation</param-name><br> <param-value>/WEB-INF/web-config.xml</param-value><br> </init-param><br> <load-on-startup>1</load-on-startup><br></servlet><br><servlet-mapping><br> <servlet-name>dispatcher</servlet-name><br> <url-pattern>/</url-pattern><br></servlet-mapping><br> |
xml<br><mvc:annotation-driven/><br><context:component-scan base-package="com.example.web"/><br><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><br> <property name="prefix" value="/WEB-INF/views/"/><br> <property name="suffix" value=".jsp"/><br></bean><br><mvc:resources mapping="/static/**" location="/static/"/><br> |
配置目的 | 告诉Servlet容器如何启动和部署Spring MVC应用 | 告诉Spring MVC框架如何处理请求、解析视图和管理组件 |
依赖关系 | 依赖于Servlet规范,与具体Servlet容器(如Tomcat)相关 | 依赖于Spring MVC框架,与具体应用逻辑相关 |
替代方案 | 在Servlet 3.0+环境中可通过实现WebApplicationInitializer接口用Java类替代 | 可通过Java配置类(如@Configuration注解的类)替代 |
1.4 控制器与数据绑定
|
|
完整调用流程
- 客户端发送 GET 请求
- 服务器处理流程
- 客户端发送 POST 请求
- 服务器处理流程
全局异常处理
|
|
2. 事件与消息(Event & Messaging):实现组件间通信与解耦
在构建复杂的企业级应用时,组件间的通信和解耦是核心挑战。Spring 提供了两种主要机制来应对:
- Spring 应用事件 (Application Events):主要用于应用内部(同一个 JVM 或 Spring IoC 容器内)的组件间轻量级通信和解耦。
- Spring 消息 (Messaging):则是一个更广泛的抽象层,用于集成外部消息中间件(如 JMS、RabbitMQ、Kafka 等),实现跨应用/跨服务的异步通信和解耦。
虽然两者都涉及“事件”或“消息”的发布与监听,但它们适用的场景和底层机制有本质区别。
2.1 Spring 应用事件(Application Events):容器内部的轻量级通知机制
Spring 应用事件是 Spring 框架提供的一种基于发布/订阅模式的内部通信机制。它允许应用程序中的组件在发生特定事件时发出通知,而其他对该事件感兴趣的组件可以监听并响应这些通知,从而实现组件间的松耦合。
-
工作原理:
- 事件定义:事件必须是
ApplicationEvent
的子类(或其代理)。它封装了事件发生时所需传递的数据。 - 事件发布:任何 Spring 管理的 Bean 都可以注入
ApplicationEventPublisher
接口,并通过其publishEvent()
方法发布事件。发布者无需知道哪些组件会监听此事件。 - 事件分发:Spring 容器内部的
ApplicationEventMulticaster
负责接收发布的事件,并将其分发给所有匹配的事件监听器。 - 事件监听:监听器可以是实现
ApplicationListener
接口的 Bean,也可以是在其方法上使用@EventListener
注解的 Spring Bean 方法。
- 事件定义:事件必须是
-
能做什么(核心应用场景):
- 组件内部解耦:当一个组件完成某项操作后,它只需发布一个事件,而无需直接调用其他依赖组件的方法。这避免了组件间的直接依赖,使得系统结构更加清晰和灵活。
- 领域事件驱动:在业务领域模型中,当发生重要的业务行为(如“订单创建成功”、“用户注册完成”)时,可以发布一个领域事件。后续的业务流程(如库存扣减、发送欢迎邮件、积分奖励等)可以通过监听这些事件来异步触发,实现业务流程的解耦和扩展。
- 审计与日志记录:在关键操作(如用户登录、数据修改)完成后发布事件,由独立的监听器负责记录审计日志或进行其他非核心业务的处理,避免核心业务逻辑与日志/审计耦合。
- 模块间通知:不同模块或子系统在同一个 Spring 应用内需要相互通知时,事件是一种简洁有效的通信方式。
-
同步 vs 异步:
- 默认同步:Spring 应用事件默认是同步调用的。这意味着事件发布者线程会阻塞,直到所有监听器都处理完事件。这保证了事件处理的顺序性,但在事件处理耗时较长时可能影响发布者的响应性能。
- 异步处理:若需异步处理以提升性能,可以在事件监听方法上添加
@Async
注解,并确保 Spring 配置中开启了@EnableAsync
。或者,你也可以自定义ApplicationEventMulticaster
,配置其使用线程池来异步分发事件。
示例:自定义事件与监听
|
|
2.2 Spring 消息(Messaging):集成企业级消息中间件的统一抽象
Spring Messaging 是 Spring 框架为解决企业级消息中间件集成问题而设计的一个统一抽象层。它建立在 Spring Core 和容器机制的基础上,通过提供标准化的接口和模板化编程,屏蔽了不同消息中间件(如 JMS、RabbitMQ、Kafka)的底层差异,使开发者能够以一致的方式处理消息通信,同时无缝整合 Spring 的事务、注解等特性,最终实现系统解耦和可扩展性的提升。
-
核心抽象接口:
Message
接口:定义统一的消息模型,包含消息体(payload
,实际传输的数据)和消息头部(headers
,消息的元数据,如消息ID、时间戳、路由键等),屏蔽了不同中间件的消息格式差异。MessageChannel
接口:抽象消息通道,用于发送和接收消息。它分为发送通道 (MessageChannel
) 和接收通道 (SubscribableChannel
),对应不同中间件的队列(Queue)或主题(Topic)模型。MessageHandler
接口:定义消息处理器,用于统一消息消费逻辑。
-
模板类:Spring 为常见的消息中间件提供了开箱即用的模板类,这些模板封装了底层中间件的连接、发送、接收等复杂操作,例如:
JmsTemplate
:封装 JMS 操作。RabbitTemplate
:封装 RabbitMQ 操作。KafkaTemplate
:封装 Kafka 操作。
通过这些抽象和模板类,开发者可以聚焦于业务逻辑,而无需关心与特定消息中间件打交道的细节。
2.2.1 JMS(Java Message Service):Java 消息服务标准
JMS(Java Message Service)是 Java EE 平台定义的消息服务标准接口。它本身不提供具体实现,而是定义了一套消息通信的规范和 API,允许应用程序通过符合 JMS 规范的消息中间件进行异步通信。
-
核心价值:
- 应用组件解耦:发送方(生产者)与接收方(消费者)无需直接依赖,通过消息队列间接通信,降低系统耦合度。
- 异步处理:发送消息后立即返回,无需等待接收方处理,提升系统吞吐量和响应速度,特别适合耗时任务(如日志记录、邮件发送、数据同步)。
- 跨平台通信:基于标准接口,支持不同技术栈(只要支持 JMS 规范)的系统进行交互。
- 流量削峰:在高并发场景下,消息中间件可以作为缓冲区,将瞬时的大量请求削平,保护后端服务。
-
JMS 的核心模型与角色:
- 消息模型:
- 点对点(P2P / Queue):消息发送到队列(Queue),每个消息仅被一个消费者接收并消费。适用于任务分配、工作流等场景。
- 发布/订阅(Pub/Sub / Topic):消息发送到主题(Topic),所有订阅了该主题的消费者均可接收同一份消息。适用于广播通知、日志分发等场景。
- 核心接口与组件:
ConnectionFactory
:用于创建与消息中间件物理连接的工厂(如 ActiveMQConnectionFactory)。Connection
:应用程序与消息中间件之间的物理连接,管理会话。Session
:会话,轻量级的单线程上下文,用于管理消息的生产、消费,并支持本地事务。MessageProducer
/MessageConsumer
:用于发送 / 接收消息的接口。Message
:表示一个消息对象,包含消息体(文本、对象、字节数组等)和头部属性(元数据)。
- 消息模型:
-
JMS 的典型应用场景:
- 系统解耦:电商平台中,订单服务完成下单后,发送订单创建消息至 JMS 队列,库存服务、物流服务、积分服务等监听该队列并各自处理,避免服务间强依赖。
- 异步任务处理:用户注册后,立即发送注册成功邮件的任务通过 JMS 异步执行,提升前端响应速度。
- 流量削峰:秒杀活动中,将大量下单请求作为消息先存入队列,后端消费者服务逐步处理,防止数据库瞬时压力过大。
- 跨系统集成:不同公司或部门的系统(即使技术栈不同),只要都实现了 JMS 规范,就可以通过 JMS 标准接口进行消息对接,例如银行与商户的支付通知。
Spring 集成 JMS 的示例解析
以 Spring Boot 集成 ActiveMQ 为例,通过代码理解 JMS 在 Spring 中的实现逻辑:
-
依赖与配置:
spring-boot-starter-activemq
引入了spring-jms
依赖,提供 Spring 对 JMS 的集成支持。@EnableJms
注解(通常由 Spring Boot 自动配置)开启 JMS 功能,Spring 会自动扫描@JmsListener
注解。
-
配置类:连接工厂与监听容器:
1 2 3 4 5 6 7 8 9 10 11 12 13
@Configuration @EnableJms // 启用JMS功能 public class JmsConfig { @Bean public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(ConnectionFactory connectionFactory) { DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); factory.setConnectionFactory(connectionFactory); // 注入Spring Boot自动配置的ConnectionFactory // 配置事务模式、并发消费者数量等 factory.setConcurrency("1-10"); // 最小1个,最大10个消费者实例 factory.setSessionTransacted(true); // 启用会话事务,消息处理失败时消息会回滚 return factory; } }
ConnectionFactory
通常由 Spring Boot 根据application.properties
中的配置自动创建(如 ActiveMQ 的ActiveMQConnectionFactory
)。DefaultJmsListenerContainerFactory
是 Spring 提供的关键组件,用于创建和管理 JMS 消息监听容器。它负责管理消费者的生命周期、线程池、以及与 Spring 事务的集成等。
-
消息发送方:
OrderSender
:1 2 3 4 5 6 7 8 9 10 11 12
@Service public class OrderSender { @Autowired private JmsTemplate jmsTemplate; // Spring自动注入JMS发送模板 public void sendOrder(Order order) { // convertAndSend 会自动序列化消息体(需Order类实现Serializable或配置MessageConverter) jmsTemplate.convertAndSend("order.queue", order); System.out.println("订单消息已发送到 order.queue: " + order.getId()); // 等价于:jmsTemplate.send("order.queue", session -> session.createObjectMessage(order)); } }
JmsTemplate
是 Spring 封装的 JMS 发送模板,它屏蔽了底层 JMS 连接、会话和消息创建的复杂细节,使得发送消息变得非常简洁。convertAndSend
方法支持多种消息类型(文本、对象、字节数组等),并依赖 Spring 的消息转换机制进行序列化。
-
消息接收方:
OrderReceiver
:1 2 3 4 5 6 7 8 9
@Component public class OrderReceiver { @JmsListener(destination = "order.queue") // 标记此方法为JMS监听器,指定监听的队列名称 public void receive(Order order) { System.out.println("接收到订单: " + order.getId() + ",开始处理..."); // 在这里处理订单逻辑(如更新库存、记录日志等业务操作) // 如果方法抛出异常,并且 setSessionTransacted(true) 已配置,消息会根据事务策略回滚到队列 } }
@JmsListener
注解是 Spring 提供的便捷方式,用于声明一个消息监听器。Spring 会自动根据该注解创建消费者,监听指定destination
(队列或主题)的消息。- 消息接收时,Spring 会自动将队列中的消息反序列化为方法参数所期望的对象类型(需保证发送方与接收方的类定义和序列化方式一致)。
-
JMS 在 Spring 中的高级特性:
- 事务管理:
- 在
JmsConfig
中设置factory.setSessionTransacted(true)
,表示 JMS 会话将参与事务。这意味着在消息处理方法执行过程中发生异常时,消息会回滚到队列,以便重新消费。 - 也可以通过
@Transactional
注解包裹消息处理逻辑,与数据库事务同步,实现本地事务与消息发送的最终一致性(如发送消息后才提交数据库事务,或反之)。
- 在
- 消息转换器(
MessageConverter
):- 自定义
MessageConverter
实现对象与 JMSMessage
之间的转换,例如将 Java 对象转换为 JSON 字符串消息,或将 JSON 字符串反序列化为 Java 对象。 - 示例:
jmsTemplate.setMessageConverter(new MappingJackson2MessageConverter());
- 自定义
- 错误处理(
ErrorHandler
):- 配置
DefaultJmsListenerContainerFactory
的setErrorHandler
属性,可以自定义处理消息消费过程中发生的异常。例如,将失败的消息发送至死信队列(Dead Letter Queue, DLQ),以便后续分析和重处理,避免消息丢失。
- 配置
- 事务管理:
-
JMS 与其他消息中间件的对比:
特性 | JMS | RabbitMQ | Kafka |
---|---|---|---|
定位 | Java 消息服务标准接口(非具体实现) | 基于 AMQP 协议的通用消息中间件 | 分布式流处理平台和高吞吐量消息队列 |
消息模型 | 点对点 (Queue), 发布/订阅 (Topic) | 多样化 (Queue, Exchange, Binding) | 发布/订阅 (Topic, Partition, Consumer Group) |
消息可靠性 | 支持事务和确认机制,通常较高 | 支持消息确认、持久化、重试,非常可靠 | 基于分区和副本的高可靠性,通过日志追加实现 |
性能 | 中规中矩,取决于具体实现 | 高 (基于轻量级协议 AMQP,擅长复杂路由) | 极高 (批量处理、顺序写入、分布式架构) |
消息顺序性 | 队列模型可保证严格顺序 | 队列模型可保证严格顺序 | 单个分区内严格顺序 |
适用场景 | 传统企业级 Java 应用集成、简单异步解耦 | 复杂业务解耦、消息路由、任务分发、长连接通信 | 大数据实时分析、日志收集、监控、事件溯源、高吞吐量消息处理 |
2.2.2 RabbitMQ (AMQP):灵活的消息路由与分发
RabbitMQ 是一个开源的、基于 AMQP (高级消息队列协议) 的消息中间件。它以其强大的路由能力、灵活的交换机类型和高可靠性而闻名,适用于复杂的异步通信和消息分发场景。
-
核心概念:
- 生产者 (Producer):发送消息的应用程序。
- 消费者 (Consumer):接收消息的应用程序。
- 队列 (Queue):存储消息的地方,消费者从队列中获取消息。
- 交换机 (Exchange):消息到达 RabbitMQ 后,首先会被发送到交换机。交换机根据自身类型和路由规则,将消息转发到绑定到它的队列。常见的交换机类型有:
- Direct Exchange (直连交换机):根据
routingKey
精确匹配。 - Fanout Exchange (扇形交换机):广播消息到所有绑定队列。
- Topic Exchange (主题交换机):根据
routingKey
的模式匹配。
- Direct Exchange (直连交换机):根据
- 绑定 (Binding):将队列绑定到交换机,并指定路由键,定义了消息如何从交换机路由到队列。
-
Spring 集成 RabbitMQ:
- 配置:引入
spring-boot-starter-amqp
或spring-rabbit
依赖,主要配置RabbitTemplate
(用于发送消息)、ConnectionFactory
(用于连接 RabbitMQ Broker)。 - 注解:使用
@RabbitListener(queues = "queueName")
接收消息,使用rabbitTemplate.convertAndSend(exchange, routingKey, message)
发送消息。
- 配置:引入
示例:RabbitMQ 发送与监听
|
|
2.2.3 Kafka:高吞吐量的分布式流处理平台
Apache Kafka 是一个分布式流处理平台,最初由 LinkedIn 开发,旨在处理大规模的实时数据流。它以其高吞吐量、低延迟、高可用性、可扩展性和持久性而闻名,特别适用于日志收集、实时数据分析、事件溯源和大数据管道等场景。
-
核心概念:
- 主题 (Topic):消息的逻辑分类,生产者将消息发布到主题,消费者从主题订阅消息。
- 分区 (Partition):每个主题可以划分为一个或多个分区。分区是 Kafka 消息存储的最小单元,消息在分区内是有序的,但不同分区之间无序。分区是 Kafka 实现高吞吐量和可伸缩性的关键。
- 生产者 (Producer):向 Kafka 主题发布消息的应用程序。
- 消费者 (Consumer):从 Kafka 主题订阅消息的应用程序。多个消费者可以组成一个消费者组 (Consumer Group),在同一个组内,每个分区只能被组内的一个消费者消费,实现负载均衡和消息不重复消费。
- Broker:Kafka 服务器实例,负责存储消息和处理生产者/消费者请求。
-
Spring 集成 Kafka:
- 配置:引入
spring-kafka
依赖,主要配置KafkaTemplate
(用于发送消息)、ConsumerFactory
和ProducerFactory
(用于创建消费者和生产者实例)。 - 注解:使用
@KafkaListener(topics = "topicName", groupId = "groupId")
监听消息。
- 配置:引入
示例:Kafka 发送与监听
|
|
总结
Spring 框架是 Java 生态的基石,必知必会。
文章作者 会写代码的小郎中
上次更新 2025-07-05
许可协议 CC BY-NC-ND 4.0