当前位置: 首页 > news >正文

再见!三层框架开发

说明:三层框架开发 是常见的 JavaWeb 开发模式,依次分为

  • Controller:接收参数,返回响应结果;

  • Service:处理业务,实现功能;

  • DAO:操作数据库;

有的项目不是严格的三层架构,可能会多一层,但这种分层开发模式还是一样的。

本文使用 commons-chain 介绍一种链式开发模式,不同于常见的三层架构开发。

三层架构写法

(1)创建项目

创建一个 Maven 项目,pom 文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.12</version><relativePath/></parent><groupId>com.hezy</groupId><artifactId>ChainDemo</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>commons-chain</groupId><artifactId>commons-chain</artifactId><version>1.2</version></dependency></dependencies>
</project>

其中,最后是后面要用到的链式写法所需要的依赖

<dependency><groupId>commons-chain</groupId><artifactId>commons-chain</artifactId><version>1.2</version>
</dependency>

(2)创建接口

一个简单的 Demo,有以下两个接口,一个存入用户对象,一个读取用户对象,

import com.hezy.pojo.User;
import com.hezy.service.general.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import java.util.List;/*** 通常操作*/
@RestController
@RequestMapping("/demo")
public class DemoController {@Autowiredprivate UserService userService;@PostMapping("/add")public List<String> addUser(@RequestBody User user) {return userService.addUser(user);}@GetMapping("/get")public User getUser(String username) {return userService.getUser(username);}
}

User 对象如下:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;/*** 用户*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {/*** 用户名*/private String username;/*** 年龄*/private Integer age;
}

(3)实现

实现接口

(UserService)

import com.hezy.pojo.User;import java.util.List;public interface UserService {List<String> addUser(User user);User getUser(String username);
}

(UserServiceImpl,实现类,用一个 Map 模拟存入 User 对象数据)

import com.hezy.pojo.User;
import lombok.Setter;
import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;import static com.hezy.common.RegexConstant.USERNAME_PATTERN;@Service
@Setter
public class UserServiceImpl implements UserService {private final static Map<String, User> USER_MAP = new HashMap<>();@Overridepublic List<String> addUser(User user) {List<String> errorList = new ArrayList<>();// 检查 User 对象是否为空if (user == null) {errorList.add("用户对象为空");return errorList;}// 检查 usernameif (user.getUsername() == null || user.getUsername().trim().isEmpty()) {errorList.add("用户名为空");return errorList;}if (!USERNAME_PATTERN.matcher(user.getUsername()).matches()) {errorList.add("用户名只允许英文大小写字母");return errorList;}// 检查 ageif (user.getAge() == null) {errorList.add("用户名年龄不能为空");return errorList;}if (user.getAge() <= 0 || user.getAge() > 100) {errorList.add("用户名年龄只能在0~100以内");return errorList;}// 存入对象USER_MAP.put(user.getUsername(), user);return null;}@Overridepublic User getUser(String username) {return USER_MAP.get(username);}
}

其中对用户名进行了校验,正则表达式如下,用户名只允许英文大小写字母

import java.util.regex.Pattern;public interface RegexConstant {/*** 正则表达式:只允许英文大小写字母*/Pattern USERNAME_PATTERN = Pattern.compile("^[a-zA-Z]+$");
}

(4)测试

启动项目,测试 Demo

(存入)

在这里插入图片描述

(读取)

在这里插入图片描述

以上是使用三层架构实现的开发模式

链式写法

下面使用 commons-chain 链式写法来实现

(1)创建处理链

创建处理链,将创建用户的步骤在这里组装完成,分为参数校验、存入用户两个步骤,对应两个 Command

import org.apache.commons.chain.impl.ChainBase;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;import javax.annotation.Resource;/*** 用户创建处理链*/
@Component
public class UserCreationChain extends ChainBase implements InitializingBean {@Resourceprivate ValidateUserCommand validateUserCommand;@Resourceprivate SaveUserCommand saveUserCommand;@Overridepublic void afterPropertiesSet() {// 参数校验addCommand(validateUserCommand);// 存入用户addCommand(saveUserCommand);// 结束addCommand(e -> PROCESSING_COMPLETE);}
}

(2)创建上下文

创建对应处理链的上下文,这个上下文与处理链没有代码上的绑定关系,可以类比三层架构的Controller 和 Service

import com.hezy.pojo.User;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.chain.impl.ContextBase;import java.util.*;/*** 用户上下文*/
@Getter
@Setter
public class UserContext extends ContextBase {/*** 模拟数据库,存储用户数据*/public static final Map<String, User> DATABASE = new Hashtable<>();/*** 参数*/private User user;/*** 错误信息*/private List<String> errorList;
}

点开继承的 ContextBase类,可以看到这是一个 Map

在这里插入图片描述

(3)创建 Command

创建处理链中的两个 Command,如下:

(校验用户,实现 Command,返回 true 表示处理链终止,false 表示继续往后执行)

import com.hezy.pojo.User;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
import org.springframework.stereotype.Component;import java.util.ArrayList;
import java.util.List;import static com.hezy.common.RegexConstant.USERNAME_PATTERN;/*** 校验用户*/
@Component
public class ValidateUserCommand implements Command {@Overridepublic boolean execute(Context context) {UserContext userContext = (UserContext)context;User user = userContext.getUser();List<String> errorList = new ArrayList<>();// 检查 User 对象是否为空if (user == null) {errorList.add("用户对象为空");userContext.setErrorList(errorList);return true;}// 检查 usernameif (user.getUsername() == null || user.getUsername().trim().isEmpty()) {errorList.add("用户名为空");userContext.setErrorList(errorList);return true;}if (!USERNAME_PATTERN.matcher(user.getUsername()).matches()) {errorList.add("用户名只允许英文大小写字母");userContext.setErrorList(errorList);return true;}// 检查 ageif (user.getAge() == null) {errorList.add("用户名年龄不能为空");userContext.setErrorList(errorList);return true;}if (user.getAge() <= 0 || user.getAge() > 100) {errorList.add("用户名年龄只能在0~100以内");userContext.setErrorList(errorList);return true;}userContext.put(user.getUsername(), user);userContext.setErrorList(null);return false;}
}

(存入对象)

import com.hezy.pojo.User;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
import org.springframework.stereotype.Component;import static com.hezy.service.chain.UserContext.DATABASE;/*** 保存用户命令*/
@Component
public class SaveUserCommand implements Command {@Overridepublic boolean execute(Context context) {UserContext userContext = (UserContext)context;User user = userContext.getUser();// 模拟保存DATABASE.put(user.getUsername(), user);return false;}
}

(4)接口

接口如下,定义上下文,将接收的参数存入到上下文中,接着执行处理链,最后返回上下文中的错误信息

import com.hezy.pojo.User;
import com.hezy.service.chain.UserContext;
import com.hezy.service.chain.UserCreationChain;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import java.util.List;import static com.hezy.service.chain.UserContext.DATABASE;/*** 用户接口*/
@RestController
@RequestMapping("/user")
public class UserController {final UserContext userContext = new UserContext();@Autowiredprivate UserCreationChain userCreationChain;@PostMapping("/add")public List<String> addUser(@RequestBody User user) throws Exception {userContext.setUser(user);userCreationChain.execute(userContext);return userContext.getErrorList() != null ? userContext.getErrorList() : null;}@GetMapping("/get")public User getUser(String username) {return DATABASE.get(username);}
}

(4)测试

启动,测试

(年龄为负数,未通过校验)

在这里插入图片描述

(没通过校验,没有走到后面的存入,所以没查出来)

在这里插入图片描述

(添加没问题)

在这里插入图片描述

(也能查出来)

在这里插入图片描述

以上就是使用 commons-chain 链式写法实现 JavaWeb 接口开发的 Demo,相比较三层架构,如果接口的实现逻辑复杂,与其在 Service 层里调来调去,或许可以考虑如何使用这种链式写法。

另外

厘清两个概念,Context、Command

Context

Context,翻译为上下文或语境,是一个抽象概念。举个例子,下面这张宣传海报

在这里插入图片描述

海报中劳动职业者有公务员、外卖员、工人、农民、老师、医生和上班族,分析这张海报工人、外卖员都是男性,医生、老师都是女性,我们能否得出结论:所有的工人、外卖员都是男性,所有的医生、老师都是女性?

不可以,因为这是一张劳动者职业健康的宣传海报,起宣传作用,这里的“宣传海报”就是上下文,是 Context。

就是说,将对象框起来,放入一些额外的信息,这些框起来的,额外的信息就是上下文。

Command

在本文介绍的链式开发,Command 翻译为命令我觉得是不合适的,命令有点抽象过头,没有体现出链式开发的具体场景。我们可以想象一个场景,领导让你去他办公室帮他找一个笔记本:

  • “桌子上有吗?你看看”

  • “我看看,唔……没看到”

  • “右手抽屉里,翻翻看”

  • “没有”

  • “那应该在背后柜子里了”

  • “……,有,是黑色的吗?”

  • “是的,你送到会议室给我”

  • “好的”

这里领导说的话,就是链式开发中的 Commond,是带有上下文的,有前后逻辑的指令,翻译为指令或许更贴切。

总结

本文使用 commons-chain 介绍了一种链式开发模式,不同于常见的三层架构开发。

http://www.xdnf.cn/news/1229869.html

相关文章:

  • Java中的sort()排序详解
  • 涉水救援机器人cad【12张】三维图+设计书明说
  • linux编译基础知识-头文件标准路径
  • 轻量级鼠标右键增强工具 MousePlus
  • eSIM技术深度解析:从物理芯片到数字革命
  • Python从入门到精通——第五章 列表与元组
  • 机器学习【五】decision_making tree
  • 深入解析Java Stream Sink接口
  • JVM学习日记(十四)Day14——性能监控与调优(一)
  • 小迪23年-22~27——php简单回顾(2)
  • IMAP电子邮件归档系统Mail-Archiver
  • rabbitmq消息队列详述
  • 【Android】使用 Intent 传递对象的两种序列化方式
  • 深度学习-模型初始化与模型构造
  • 高性能MCP服务器架构设计:并发、缓存与监控
  • 【NLP舆情分析】基于python微博舆情分析可视化系统(flask+pandas+echarts) 视频教程 - 微博舆情数据可视化分析-热词情感趋势树形图
  • 【机器学习】非线性分类算法详解(下):决策树(最佳分裂特征选择的艺术)与支持向量机(最大间隔和核技巧)
  • 在 AKS 中运行 Azure DevOps 私有代理-1
  • Linux性能监控与调优全攻略
  • React ahooks——副作用类hooks之useThrottleFn
  • React ahooks——副作用类hooks之useDebounceFn
  • Shell【脚本 02】离线安装配置Zookeeper及Kafka并添加service服务和开机启动(脚本分析)
  • 堆----1.数组中的第K个最大元素
  • 通过filezilla在局域网下实现高速传输数据
  • 2025-08 安卓开发面试拷打记录(面试题)
  • 【龙泽科技】汽车故障诊断仿真教学软件【风光580】
  • Vue 详情模块 4
  • Python科研数据可视化技术
  • 知识蒸馏 - 基于KL散度的知识蒸馏 HelloWorld 示例
  • 在 AKS 中运行 Azure DevOps 自托管代理-2