基于 Spring 实现管道模式的最佳实践,2024年最新面试必知必会的问题

private String errorMsg;

// 其他参数

@Override

public String getName() {

return “模型实例构建上下文”;

}

}

处理器 - 输入数据校验:

@Component

public class InputDataPreChecker implements ContextHandler {

private final Logger logger = LoggerFactory.getLogger(this.getClass());

@Override

public boolean handle(InstanceBuildContext context) {

logger.info(“–输入数据校验–”);

Map<String, Object> formInput = context.getFormInput();

if (MapUtils.isEmpty(formInput)) {

context.setErrorMsg(“表单输入数据不能为空”);

return false;

}

String instanceName = (String) formInput.get(“instanceName”);

if (StringUtils.isBlank(instanceName)) {

context.setErrorMsg(“表单输入数据必须包含实例名称”);

return false;

}

return true;

}

}

处理器 - 根据输入创建模型实例:

@Component

public class ModelInstanceCreator implements ContextHandler {

private final Logger logger = LoggerFactory.getLogger(this.getClass());

@Override

public boolean handle(InstanceBuildContext context) {

logger.info(“–根据输入数据创建模型实例–”);

// 假装创建模型实例

return true;

}

}

处理器 - 保存模型实例到相关DB表:

@Component

public class ModelInstanceSaver implements ContextHandler {

private final Logger logger = LoggerFactory.getLogger(this.getClass());

@Override

public boolean handle(InstanceBuildContext context) {

logger.info(“–保存模型实例到相关DB表–”);

// 假装保存模型实例

return true;

}

}

到这里,有个问题就出现了:应该使用什么样的方式,将同一种 Context 的 ContextHandler 串联为管道呢?思考一下:

  1. 给 ContextHandler 加一个 setNext 方法,每个实现类必须指定其下一个处理器。缺点也很明显,如果在当前管道中间加入一个新的 ContextHandler,那么要势必要修改前一个 ContextHandler 的 setNext 方法;另外,代码是写给人阅读的,这样做没法一眼就直观的知道整个管道的处理链路,还要进入到每个相关的 ContextHandler 中去查看才知道。

  2. 给 ContextHandler 加上 @Order 注解,根据 @Order 中给定的数字来确定每个 ContextHandler 的序列,一开始时每个数字间隔的可以大些(比如 10、20、30),后续加入新的 ContextHandler 时,可以指定数字为 (11、21、31)这种,那么可以避免上面方案中要修改代码的问题,但是仍然无法避免要进入每个相关的 ContextHandler 中去查看才能知道管道处理链路的问题。

  3. 提前写好一份路由表,指定好 ”Context -> 管道“ 的映射(管道用 List 来表示),以及管道中处理器的顺序 。Spring 来根据这份路由表,在启动时就构建好一个 Map,Map 的键为 Context 的类型,值为 管道(即 List)。这样的话,如果想知道每个管道的处理链路,直接看这份路由表就行,一目了然。缺点嘛,就是每次加入新的 ContextHandler 时,这份路由表也需要在对应管道上进行小改动 —— 但是如果能让阅读代码更清晰,我觉得这样的修改是值得的、可接受的~

基于 Spring 实现管道模式的最佳实践

 构建管道路由表

=================

基于 Spring 的 Java Bean 配置,我们可以很方便的构建管道的路由表:

/**

  • 管道路由的配置

*/

@Configuration

public class PipelineRouteConfig implements ApplicationContextAware {

/**

  • 数据类型->管道中处理器类型列表 的路由

*/

private static final

Map<Class<? extends PipelineContext>,

List<Class<? extends ContextHandler<? extends PipelineContext>>>> PIPELINE_ROUTE_MAP = new HashMap<>(4);

/*

  • 在这里配置各种上下文类型对应的处理管道:键为上下文类型,值为处理器类型的列表

*/

static {

PIPELINE_ROUTE_MAP.put(InstanceBuildContext.class,

Arrays.asList(

InputDataPreChecker.class,

ModelInstanceCreator.class,

ModelInstanceSaver.class

));

// 将来其他 Context 的管道配置

}

/**

  • 在 Spring 启动时,根据路由表生成对应的管道映射关系

*/

@Bean(“pipelineRouteMap”)

public Map<Class<? extends PipelineContext>, List<? extends ContextHandler<? extends PipelineContext>>> getHandlerPipelineMap() {

return PIPELINE_ROUTE_MAP.entrySet()

.stream()

.collect(Collectors.toMap(Map.Entry::getKey, this::toPipeline));

}

/**

  • 根据给定的管道中 ContextHandler 的类型的列表,构建管道

*/

private List<? extends ContextHandler<? extends PipelineContext>> toPipeline(

Map.Entry<Class<? extends PipelineContext>, List<Class<? extends ContextHandler<? extends PipelineContext>>>> entry) {

return entry.getValue()

.stream()

.map(appContext::getBean)

.collect(Collectors.toList());

}

private ApplicationContext appContext;

@Override

public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

appContext = applicationContext;

}

}

 定义管道执行器

=================

最后一步,定义管道执行器。管道执行器 根据传入的上下文数据的类型,找到其对应的管道,然后将上下文数据放入管道中去进行处理。

/**

  • 管道执行器

*/

@Component

public class PipelineExecutor {

private final Logger logger = LoggerFactory.getLogger(this.getClass());

/**

  • 引用 PipelineRouteConfig 中的 pipelineRouteMap

*/

@Resource

private Map<Class<? extends PipelineContext>,

List<? extends ContextHandler<? super PipelineContext>>> pipelineRouteMap;

/**

  • 同步处理输入的上下文数据

  • 如果处理时上下文数据流通到最后一个处理器且最后一个处理器返回 true,则返回 true,否则返回 false

  • @param context 输入的上下文数据

  • @return 处理过程中管道是否畅通,畅通返回 true,不畅通返回 false

*/

public boolean acceptSync(PipelineContext context) {

Objects.requireNonNull(context, “上下文数据不能为 null”);

// 拿到数据类型

Class<? extends PipelineContext> dataType = context.getClass();

// 获取数据处理管道

List<? extends ContextHandler<? super PipelineContext>> pipeline = pipelineRouteMap.get(dataType);

if (CollectionUtils.isEmpty(pipeline)) {

logger.error(“{} 的管道为空”, dataType.getSimpleName());

return false;

}

// 管道是否畅通

boolean lastSuccess = true;

for (ContextHandler<? super PipelineContext> handler : pipeline) {

try {

// 当前处理器处理数据,并返回是否继续向下处理

lastSuccess = handler.handle(context);

} catch (Throwable ex) {

lastSuccess = false;

logger.error(“[{}] 处理异常,handler={}”, context.getName(), handler.getClass().getSimpleName(), ex);

}

// 不再向下处理

if (!lastSuccess) { break; }

}

return lastSuccess;

}

}

 使用管道模式

================

此时,我们可以将最开始的 buildModelInstance 修改为:

public CommonResponse buildModelInstance(InstanceBuildRequest request) {

InstanceBuildContext data = createPipelineData(request);

boolean success = pipelineExecutor.acceptSync(data);

// 创建模型实例成功

if (success) {

return CommonResponse.success(data.getInstanceId());

}

logger.error(“创建模式实例失败:{}”, data.getErrorMsg());

return CommonResponse.failed(data.getErrorMsg());

}

我们模拟一下模型实例的创建过程:

参数正常时:

基于 Spring 实现管道模式的最佳实践

参数出错时:

这个时候我们再为 InstanceBuildContext 加入新的两个 ContextHandler:FormInputPreprocessor(表单输入数据预处理) 和 BizSideCustomProcessor(业务方自定义数据处理)。

@Component

public class FormInputPreprocessor implements ContextHandler {

private final Logger logger = LoggerFactory.getLogger(this.getClass());

@Override

public boolean handle(InstanceBuildContext context) {

logger.info(“–表单输入数据预处理–”);

// 假装进行表单输入数据预处理

return true;

}

}

@Component

public class BizSideCustomProcessor implements ContextHandler {

private final Logger logger = LoggerFactory.getLogger(this.getClass());

@Override

public boolean handle(InstanceBuildContext context) {

logger.info(“–业务方自定义数据处理–”);

// 先判断是否存在自定义数据处理,如果没有,直接返回 true

// 调用业务方的自定义的表单数据处理

return true;

}

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
img

最后

分享一套我整理的面试干货,这份文档结合了我多年的面试官经验,站在面试官的角度来告诉你,面试官提的那些问题他最想听到你给他的回答是什么,分享出来帮助那些对前途感到迷茫的朋友。

面试经验技巧篇
  • 经验技巧1 如何巧妙地回答面试官的问题
  • 经验技巧2 如何回答技术性的问题
  • 经验技巧3 如何回答非技术性问题
  • 经验技巧4 如何回答快速估算类问题
  • 经验技巧5 如何回答算法设计问题
  • 经验技巧6 如何回答系统设计题
  • 经验技巧7 如何解决求职中的时间冲突问题
  • 经验技巧8 如果面试问题曾经遇见过,是否要告知面试官
  • 经验技巧9 在被企业拒绝后是否可以再申请
  • 经验技巧10 如何应对自己不会回答的问题
  • 经验技巧11 如何应对面试官的“激将法”语言
  • 经验技巧12 如何处理与面试官持不同观点这个问题
  • 经验技巧13 什么是职场暗语

面试真题篇
  • 真题详解1 某知名互联网下载服务提供商软件工程师笔试题
  • 真题详解2 某知名社交平台软件工程师笔试题
  • 真题详解3 某知名安全软件服务提供商软件工程师笔试题
  • 真题详解4 某知名互联网金融企业软件工程师笔试题
  • 真题详解5 某知名搜索引擎提供商软件工程师笔试题
  • 真题详解6 某初创公司软件工程师笔试题
  • 真题详解7 某知名游戏软件开发公司软件工程师笔试题
  • 真题详解8 某知名电子商务公司软件工程师笔试题
  • 真题详解9 某顶级生活消费类网站软件工程师笔试题
  • 真题详解10 某知名门户网站软件工程师笔试题
  • 真题详解11 某知名互联网金融企业软件工程师笔试题
  • 真题详解12 国内某知名网络设备提供商软件工程师笔试题
  • 真题详解13 国内某顶级手机制造商软件工程师笔试题
  • 真题详解14 某顶级大数据综合服务提供商软件工程师笔试题
  • 真题详解15 某著名社交类上市公司软件工程师笔试题
  • 真题详解16 某知名互联网公司软件工程师笔试题
  • 真题详解17 某知名网络安全公司校园招聘技术类笔试题
  • 真题详解18 某知名互联网游戏公司校园招聘运维开发岗笔试题

资料整理不易,点个关注再走吧

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
img

  • 真题详解1 某知名互联网下载服务提供商软件工程师笔试题
  • 真题详解2 某知名社交平台软件工程师笔试题
  • 真题详解3 某知名安全软件服务提供商软件工程师笔试题
  • 真题详解4 某知名互联网金融企业软件工程师笔试题
  • 真题详解5 某知名搜索引擎提供商软件工程师笔试题
  • 真题详解6 某初创公司软件工程师笔试题
  • 真题详解7 某知名游戏软件开发公司软件工程师笔试题
  • 真题详解8 某知名电子商务公司软件工程师笔试题
  • 真题详解9 某顶级生活消费类网站软件工程师笔试题
  • 真题详解10 某知名门户网站软件工程师笔试题
  • 真题详解11 某知名互联网金融企业软件工程师笔试题
  • 真题详解12 国内某知名网络设备提供商软件工程师笔试题
  • 真题详解13 国内某顶级手机制造商软件工程师笔试题
  • 真题详解14 某顶级大数据综合服务提供商软件工程师笔试题
  • 真题详解15 某著名社交类上市公司软件工程师笔试题
  • 真题详解16 某知名互联网公司软件工程师笔试题
  • 真题详解17 某知名网络安全公司校园招聘技术类笔试题
  • 真题详解18 某知名互联网游戏公司校园招聘运维开发岗笔试题

[外链图片转存中…(img-w5bH3cyd-1712716437198)]

资料整理不易,点个关注再走吧

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-qyCCR5wF-1712716437199)]

  • 26
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值