在 Java 开发领域,选择合适的架构模式对于构建高效、可维护且能满足业务需求的软件系统至关重要。本文将深入探讨几种常见的 Java架构模式,包括单体架构与微服务架构、分层架构与微服务架构的对比,以及事件驱动架构与CQRS(命令与查询职责分离),通过源码解读、分析实现原理、探讨性能特点以及呈现应用场景等方面,帮助大家更好地理解和应用这些架构模式。
无套路、关注即可领。持续更新中
关注公众号:搜 【架构研究站】 回复:资料领取,即可获取全部面试题以及1000+份学习资料
单体架构是将一个应用的所有功能模块都打包在一个单一的代码库中,作为一个整体进行部署和运行。例如,一个简单的电商系统,其用户管理、商品管理、订单处理等功能模块的代码都在同一个项目里,通过不同的类和方法来实现各自的功能,最终打包成一个可执行的应用程序(如一个 WAR 包部署在 Tomcat 服务器上)。
在单体架构中,各个功能模块之间通常通过方法调用、类的依赖关系来交互。以一个包含用户登录和订单查询功能的单体应用为例,代码结构可能如下:
// 用户服务类,处理用户相关逻辑,比如登录验证等
class UserService {
public boolean login(String username, String password) {
// 模拟从数据库查询用户信息进行验证的逻辑
User user = UserRepository.findByUsername(username);
if (user!= null && user.getPassword().equals(password)) {
return true;
}
return false;
}
}
// 订单服务类,负责订单查询等操作
class OrderService {
public List<Order> getOrdersByUserId(int userId) {
// 从数据库获取指定用户的订单列表
return OrderRepository.findByUserId(userId);
}
}
// 模拟数据访问层,这里简单使用静态方法模拟从内存数据结构获取数据(实际会连接数据库)
class UserRepository {
private static Map<String, User> userMap = new HashMap<>();
static {
// 初始化一些用户数据
userMap.put("user1", new User("user1", "password1"));
}
public static User findByUsername(String username) {
return userMap.get(username);
}
}
class OrderRepository {
private static Map<Integer, List<Order>> orderMap = new HashMap<>();
static {
// 初始化一些订单数据
List<Order> orders = new ArrayList<>();
orders.add(new Order(1, 1, "商品1", 100));
orderMap.put(1, orders);
}
public static List<Order> findByUserId(int userId) {
return orderMap.get(userId);
}
}
// 简单的用户类和订单类定义
class User {
private String username;
private String password;
public User(String username, String password) {
this.username = username;
this.password = password;
}
// 省略Getter和Setter方法
}
class Order {
private int id;
private int userId;
private String productName;
private double price;
public Order(int id, int userId, String productName, double price) {
this.id = id;
this.userId = userId;
this.productName = productName;
this.price = price;
}
// 省略Getter和Setter方法
}
// 模拟的控制器类,接收外部请求并调用相应服务
class MainController {
public static void main(String[] args) {
UserService userService = new UserService();
if (userService.login("user1", "password1")) {
OrderService orderService = new OrderService();
List<Order> orders = orderService.getOrdersByUserId(1);
for (Order order : orders) {
System.out.println("订单信息: " + order.getProductName() + ", 价格: " + order.getPrice());
}
}
}
}
在这个示例中,UserService 通过调用 UserRepository 的方法获取用户信息进行登录验证,OrderService 调用 OrderRepository 获取订单数据,整体在一个 main 方法中模拟了请求处理流程,体现了单体架构下各功能模块紧密耦合在一个代码库中的特点。
优点:
微服务架构是将一个大型的应用拆分成多个小型的、独立的服务,每个服务都有自己独立的业务功能、数据库(可以是独立的数据库实例,也可以是同一个数据库中的不同 schema 等方式),并且可以独立开发、部署、运行和扩展。例如,同样是电商系统,用户服务、商品服务、订单服务、支付服务等都可以作为独立的微服务存在,它们通过轻量级的通信机制(如 RESTful API、消息队列等)进行交互。
以使用 Spring Boot 构建的简单用户服务微服务和订单服务微服务之间的交互为例:
用户服务微服务(UserService):
import org.springframework.boot.SpringBootApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
@SpringBootApplication
@RestController
public class UserServiceApplication {
// 模拟用户数据存储,实际会使用数据库
private static List<User> users = new ArrayList<>();
static {
users.add(new User(1, "user1", "password1"));
}
@GetMapping("/users/{id}")
public User getUserById(@PathVariable int id) {
for (User user : users) {
if (user.getId() == id) {
return user;
}
}
return null;
}
public static void main(String[] args) {
SpringBootApplication.run(UserServiceApplication.class, args);
}
// 用户类定义
static class User {
private int id;
private String username;
private String password;
public User(int id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
// 省略Getter和Setter方法
}
}
订单服务微服务(OrderService):
import org.springframework.boot.SpringBootApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
@SpringBootApplication
@RestController
public class OrderServiceApplication {
// 模拟订单数据存储,实际会使用数据库
private static List<Order> orders = new ArrayList<>();
static {
orders.add(new Order(1, 1, "商品1", 100));
}
@GetMapping("/orders/{userId}")
public List<Order> getOrdersByUserId(@PathVariable int userId) {
List<Order> userOrders = new ArrayList<>();
for (Order order : orders) {
if (order.getUserId() == userId) {
userOrders.add(order);
}
}
return userOrders;
}
public static void main(String[] args) {
SpringBootApplication.run(OrderServiceApplication.class, args);
}
// 订单类定义
static class Order {
private int id;
private int userId;
private String productName;
private double price;
public Order(int id, int userId, String productName, double price) {
this.id = id;
this.userId = userId;
public String getProductName() {
return productName;
}
public double getPrice() {
return price;
}
}
}
在这个示例中,订单服务微服务如果需要获取用户信息来关联订单,可以通过发送 HTTP 请求(如使用 RestTemplate 或者 WebClient 等客户端工具)到用户服务微服务的 /users/{id} 端点获取相应的用户数据,实现了不同微服务之间基于 RESTful API 的交互,体现了微服务独立开发、通过网络通信协作的特点。
优点:
分层架构是将软件系统按照不同的职责和功能划分为多个层次,常见的有三层架构(表示层、业务逻辑层、数据访问层),也有多层架构(如在业务逻辑层再细分等情况)。各层之间通过接口进行交互,下层为上层提供服务,上层调用下层的接口来实现业务功能。例如,在一个 Java Web 应用中,用户在浏览器发起请求,先到达表示层(如 Spring MVC 中的控制器层),控制器调用业务逻辑层的服务来处理业务,业务逻辑层再通过数据访问层与数据库交互获取或存储数据。
以一个简单的员工管理系统为例,展示三层架构的代码实现:
表示层(EmployeeController):
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
@Controller
@RequestMapping("/employees")
public class EmployeeController {
@Resource
private EmployeeService employeeService;
@GetMapping("/")
public List<Employee> getEmployees() {
return employeeService.getAllEmployees();
}
@PostMapping("/")
public void addEmployee(@RequestBody Employee employee) {
employeeService.addEmployee(employee);
}
}
业务逻辑层(EmployeeService):
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
@Service
public class EmployeeService {
@Resource
private EmployeeRepository employeeRepository;
public List<Employee> getAllEmployees() {
return employeeRepository.findAll();
}
public void addEmployee(Employee employee) {
employeeRepository.save(employee);
}
}
数据访问层(EmployeeRepository):
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
}
在这个示例中,EmployeeController 作为表示层接收外部请求,调用 EmployeeService 业务逻辑层的方法来处理业务,EmployeeService 再通过 EmployeeRepository 数据访问层与数据库交互,体现了分层架构各层之间清晰的依赖和调用关系。
优点:
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: order-service-container
image: order-service:latest
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: order-service
spec:
selector:
app: order-service
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: ClusterIP
这个配置定义了订单服务微服务的 Deployment(部署三个副本)和 Service(用于服务发现和负载均衡),可以方便地进行独立部署和管理,当需要更新时,只需要更新该微服务对应的 Docker 镜像并更新 Deployment 资源即可,不会影响其他微服务。
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class OrderProcessingService {
@Transactional
public void processOrder(Order order, Inventory inventory) {
// 保存订单操作
orderRepository.save(order);
// 更新库存操作
inventoryRepository.updateStock(inventory);
}
}
在这个示例中,当 processOrder 方法执行时,orderRepository.save(order) 和 inventoryRepository.updateStock(inventory) 会在一个事务中执行,如果其中一个操作失败,整个事务会回滚,保证了数据的一致性。
// 订单服务发送事件的代码示例
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.beans.factory.annotation.Autowired;
public class OrderService {
@Autowired
private KafkaTemplate<String, OrderEvent> kafkaTemplate;
public void createOrder(Order order) {
// 保存订单操作
orderRepository.save(order);
// 发送订单创建事件
OrderEvent event = new OrderEvent(order.getId(), "ORDER_CREATED");
kafkaTemplate.send("order-events", event);
}
}
// 库存服务订阅事件的代码示例
import org.springframework.kafka.annotation.KafkaListener;
public class InventoryService {
@KafkaListener(topics = "order-events", groupId = "inventory-group")
public void listen(OrderEvent event) {
if (event.getEventType().equals("ORDER_CREATED")) {
// 根据订单事件更新库存
updateInventory(event.getOrderId());
}
}
private void updateInventory(int orderId) {
// 实际更新库存的逻辑
}
}
这种基于事件驱动的方式通过消息队列将数据一致性的维护解耦,各个微服务可以异步处理,虽然增加了系统的复杂性,但提高了系统的灵活性和可扩展性。
事件驱动架构是一种系统设计模式,其中系统的各个组件之间通过事件进行通信。组件可以是微服务、模块或不同的系统,当一个组件发生某些动作时,会产生一个事件,其他订阅该事件的组件会接收到这个事件并做出相应的反应。这种架构模式可以使系统具有松耦合、可扩展性和高响应性的特点。
以一个用户注册的事件驱动流程为例,使用 Apache Kafka 作为事件代理:
用户服务产生事件:
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private KafkaTemplate<String, UserEvent> kafkaTemplate;
public void registerUser(User user) {
// 模拟保存用户信息的逻辑
saveUser(user);
// 发送用户注册事件
UserEvent event = new UserEvent(user.getId(), "USER_REGISTERED");
kafkaTemplate.send("user-events", event);
}
private void saveUser(User user) {
// 实际保存用户的逻辑,例如存储到数据库中
}
}
其他服务订阅事件:
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Service;
@Service
public class EmailService {
@KafkaListener(topics = "user-events", groupId = "email-group")
public void onUserRegistered(UserEvent event) {
if (event.getEventType().equals("USER_REGISTERED")) {
// 发送欢迎邮件的逻辑
sendWelcomeEmail(event.getUserId());
}
}
private void sendWelcomeEmail(int userId) {
// 实际发送邮件的逻辑
}
}
在这个例子中,当用户服务 UserService 完成用户注册操作后,会产生一个 USER_REGISTERED 类型的事件,并将其发送到 Kafka 的 user-events 主题,而 EmailService 服务订阅了 user-events 主题,当收到该事件时,会触发 sendWelcomeEmail 方法发送欢迎邮件,实现了不同服务之间基于事件的通信和协作。
优点:
缺点:
CQRS 将应用程序的操作分为两类:命令(Command)和查询(Query),命令用于修改数据(如创建、更新、删除),而查询用于读取数据。通常将命令和查询的处理逻辑分离到不同的类或服务中,甚至可以使用不同的数据库,以实现更好的性能和扩展性。
以下是一个简单的 CQRS 实现示例:
命令处理端(Command Side):
import org.springframework.stereotype.Service;
@Service
public class OrderCommandService {
private OrderRepository orderRepository;
public OrderCommandService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
public void createOrder(OrderCreateCommand command) {
Order order = new Order(command.getOrderId(), command.getUserId(), command.getProductId());
orderRepository.save(order);
}
}
查询处理端(Query Side):
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class OrderQueryService {
private OrderReadRepository orderReadRepository;
public OrderQueryService(OrderReadRepository orderReadRepository) {
this.orderReadRepository = orderReadRepository;
}
public List<Order> getOrdersByUserId(int userId) {
return orderReadRepository.findByUserId(userId);
}
}
命令和查询的接口定义:
// 命令接口
public interface Command {
}
public class OrderCreateCommand implements Command {
private int orderId;
private int userId;
private int productId;
public OrderCreateCommand(int orderId, int userId, int productId) {
this.orderId = orderId;
this.userId = userId;
this.productId = productId;
}
// 省略Getter和Setter方法
}
// 查询接口
public interface Query<T> {
T execute();
}
public class OrderQueryByUserId implements Query<List<Order>> {
private int userId;
public OrderQueryByUserId(int userId) {
this.userId = userId;
}
@Override
public List<Order> execute() {
// 实际的查询逻辑,例如调用 OrderQueryService
return orderQueryService.getOrdersByUserId(userId);
}
}
在这个示例中,OrderCommandService 专门负责处理订单创建等修改数据的命令操作,而 OrderQueryService 专门负责查询操作,并且可以根据不同的查询需求,定义不同的查询类(如 OrderQueryByUserId),甚至可以将读操作和写操作的数据存储分离,比如写操作使用关系型数据库,读操作使用专门的查询优化的数据库(如 Elasticsearch 等),提高查询性能。
优点:
缺点:
不同的 Java架构模式各有其优缺点和适用场景,在选择架构模式时,需要综合考虑业务规模、业务发展速度、团队能力、性能需求、可维护性等多方面因素。单体架构适合小型简单的项目,而微服务架构适用于大型复杂的、需要高度可扩展性和灵活性的系统;分层架构提供了清晰的代码结构和相对简单的开发维护方式,适用于传统的企业应用;事件驱动架构和CQRS则更侧重于系统的灵活性、性能优化和解耦,适用于复杂的业务流程和需要高性能的读写分离场景。在实际项目中,可能会结合多种架构模式,例如在微服务架构中使用事件驱动架构进行服务间的通信,或者在分层架构的基础上使用CQRS优化某些功能模块。作为开发人员和架构师,需要根据具体情况灵活运用这些架构模式,为系统构建一个高效、稳定且可扩展的架构,以满足业务发展和用户的需求。