架构简洁之道:从阿里开源应用架构COLA说起

2023-11-13

1.png

导读:COLA 的主要目的是为应用架构提供一套简单的可以复制、可以理解、可以落地、可以控制复杂性的”指导和约束"。在实践中作者发现 COLA 在简洁性上仍有不足,因此给 COLA 做了一次“升级”,在这次升级中,没有增加任何新的功能,而是尽量多删减了一些概念和功能,让 COLA 更简洁有效。

最近,同事告诉我,COLA 作为应用架构,已经被选入阿里云的 Java 应用初始化的应用架构选项之一。

2.png

This is really something,于是,在这个里程碑节点上,我开始回过头来,重新审视 COLA 一路走来的得与失。

COLA 作为一种架构思想无疑是成功的。但是作为框架,个人感觉有点鸡肋之嫌。特别是在简洁性上做的不好,感觉做了不少画蛇添足的事情。

试想一下,有些功能我作为作者都很少去使用,我实在想不到,它为什么还有存在的理由。

基于上面的思考,我做了这一次 COLA 2.0 到 COLA 3.0 的升级。在本次升级中,我没有增加任何新的功能,而是尽量多删减了一些概念和功能。让 COLA 可以更加纯粹的 focus 在应用架构上,而不是框架支持和架构约束上。

支持我做这些决策的背后原因只有一个——奥卡姆剃刀原理。

奥卡姆剃刀原理

奥卡姆剃刀原理,是指如无必要,勿增实体(Entities should not be multiplied unnecessarily),即“简单有效原理”。正如奥卡姆在《箴言书注》2 卷 15 题说“切勿浪费较多东西去做,用较少的东西,同样可以做好的事情。”

在具体的应用过程中,我们可以遵循以下原则去做事情:

“如果同一个现象有 n 种理论,最简单的那个便是最正确的。能用 n 做好事情,那就不要有第 n+1 个动作。”

比如,《皇帝的新衣》的皇帝到底穿没穿衣服呢?如果你在现场,你很有可能就是大臣之一。

如果懂得了奥卡姆剃刀原理,可以用逻辑手段,判断谁是真理。

  • 第一种逻辑如下:假设皇帝是真的穿了衣服→假设愚蠢的人看不见→假设你就是愚蠢的人→所以你没看见皇帝穿衣服;
  • 第二种逻辑如下:假设皇帝没穿衣服→所以你没看见皇帝穿衣服。

同样看见光身子的皇帝,第二种解释简单明了。而第一种解释,可能正因为它是错误的,就需要更多假设来补救漏洞,就像说谎圆谎一样。

真相不需要伪装掩饰,简单明了。

再比如,地心说和日心说,托勒密的地心说模型是一个本轮均轮模型。人们可以按照这个模型,定量计算行星的运动,据此推测行星所在的位置。

到了中世纪后期随着观察仪器的不断改进,人们能够更加精确地测量出行星的位置和运动,观测到行星实际位置与这个模型的计算结果存在偏差,一开始还能勉强应付,后来小本轮增加到八十多个,却仍然不能精确地计算出行星的准确位置。

1543 年,波兰天文学家哥白尼在临终时发表了一部具有历史意义的著作——《天体运行论》。这个理论体系提出了一个明确的观点:太阳是宇宙的中心,一切行星都在围绕太阳旋转。该理论认为,地球也是行星之一,它一方面像陀螺一样自转,一方面又和其他行星一样围绕太阳转动。

3.png

哥白尼的计算不仅结构严谨,而且计算简单,与已经加到八十余个圈的地心说相比,哥白尼的计算与实际观测资料能更好地吻合。因此,地心说最终被日心说所取代。

设计中的弯弯绕

深入考察一下,我们系统中,类似于“地心说”这样的弯弯绕的设计,实在是不在少数。

从系统架构的角度看,有些弯弯绕是因为系统边界划分不合理,导致职责不清,依赖混乱。

从应用架构的角度看,有些弯弯绕是因为过度设计,为了追求所谓的灵活性和可扩展性,使用了不恰当的设计。导致本来可以直观呈现的代码逻辑,被各种包装,各种隐藏,各种转发.... 无形中极大的阻碍了代码的可读性和可理解性,增加了维护成本。

举个例子,我看过无数的业务系统,喜欢拿业务流程编排说事情。因此,在业务系统中,可以看到各种五花八门的“弯弯绕设计”。

比如,在一个业务系统中,我看到了如下的 pipeline 设计。这个设计的本质是说把一个复杂的业务操作进行结构化拆解为多个小的处理单元。

4.png

拆解是正确的,但是这种处理方式显然不够“奥卡姆”(关于更多结构化分解的内容,可以看我的另一篇文章:如何写复杂业务代码?)。作为维护人员,进入“入口函数”后,还要去查数据库,然后才能知道哪些组件被调用了,太绕了,不够直观,也不简洁。

同样的逻辑,按照下面的方式写不香吗?

public class CreateCSPUExecutor {
    @Resource
    private InitContextStep initContextStep;
    @Resource
    private CheckRequiredParamStep checkRequiredParamStep;
    @Resource
    private CheckUnitStep checkUnitStep;
    @Resource
    private CheckExpiringDateStep checkExpiringDateStep;
    @Resource
    private CheckBarCodeStep checkBarCodeStep;
    @Resource
    private CheckBarCodeImgStep checkBarCodeImgStep;
    @Resource
    private CheckBrandCategoryStep checkBrandCategoryStep;
    @Resource
    private CheckProductDetailStep checkProductDetailStep;
    @Resource
    private CheckSpecImgStep checkSpecImgStep;
    @Resource
    private CreateCSPUStep createCSPUStep;
    @Resource
    private CreateCSPULogStep createCSPULogStep;
    @Resource
    private SendCSPUCreatedEventStep sendCSPUCreatedEventStep;
    public Long create(MyCspuSaveParam myCspuSaveParam){
        SaveCSPUContext context = initContextStep.initContext(myCspuSaveParam);
        checkRequiredParamStep.check(context);
        checkUnitStep.check(context);
        checkExpiringDateStep.check(context);
        checkBarCodeStep.check(context);
        checkBarCodeImgStep.check(context);
        checkBrandCategoryStep.check(context);
        checkProductDetailStep.check(context);
        checkSpecImgStep.check(context);
        createCSPUStep.create(context);
        createCSPULogStep.log(context);
        sendCSPUCreatedEventStep.sendEvent(context);
        return context.getCspu().getId();
    }
}
public class CreateCSPUExecutor {
    @Resource
    private InitContextStep initContextStep;

    @Resource
    private CheckRequiredParamStep checkRequiredParamStep;

    @Resource
    private CheckUnitStep checkUnitStep;

    @Resource
    private CheckExpiringDateStep checkExpiringDateStep;

    @Resource
    private CheckBarCodeStep checkBarCodeStep;

    @Resource
    private CheckBarCodeImgStep checkBarCodeImgStep;

    @Resource
    private CheckBrandCategoryStep checkBrandCategoryStep;

    @Resource
    private CheckProductDetailStep checkProductDetailStep;

    @Resource
    private CheckSpecImgStep checkSpecImgStep;

    @Resource
    private CreateCSPUStep createCSPUStep;

    @Resource
    private CreateCSPULogStep createCSPULogStep;

    @Resource
    private SendCSPUCreatedEventStep sendCSPUCreatedEventStep;


    public Long create(MyCspuSaveParam myCspuSaveParam){
        SaveCSPUContext context = initContextStep.initContext(myCspuSaveParam);

        checkRequiredParamStep.check(context);

        checkUnitStep.check(context);

        checkExpiringDateStep.check(context);

        checkBarCodeStep.check(context);

        checkBarCodeImgStep.check(context);

        checkBrandCategoryStep.check(context);

        checkProductDetailStep.check(context);

        checkSpecImgStep.check(context);

        createCSPUStep.create(context);

        createCSPULogStep.log(context);

        sendCSPUCreatedEventStep.sendEvent(context);

        return context.getCspu().getId();
    }
}

这种写法简单直观,易维护,与前一种方式相比,具有同样的组件复用性。符合奥卡姆剃刀的精神,相比较而言,前面那种弯弯绕设计,虽然看起来有点设计感,带来了一点点 OCP 的好处。但是无端增加了理解和认知成本,孰优孰劣,不难分辨。

COLA 3.0 升级

做了这么长的铺垫,终于到了批斗 COLA 中“弯弯绕设计”的时候了。

1. 去掉 Command

在 COLA 的初始阶段,因为受到 CQRS 的影响,于是想到了使用命令模式来处理用户请求。设计的初衷是想通过框架,一方面强制约束 Command 和 Query 的处理方式,另一方面把 Service 里面的逻辑,强制拆分到 CommandExecutor 中去,防止 Service 膨胀过快。

和上面介绍过的 pipeline 设计类似,这种设计有点绕,不够直观,如下所示:

public class MetricsServiceImpl implements MetricsServiceI{

    @Autowired
    private CommandBusI commandBus;

    @Override
    public Response addATAMetric(ATAMetricAddCmd cmd) {
        return commandBus.send(cmd);
    }

    @Override
    public Response addSharingMetric(SharingMetricAddCmd cmd) {
        return commandBus.send(cmd);
    }

    @Override
    public Response addPatentMetric(PatentMetricAddCmd cmd) {
        return  commandBus.send(cmd);
    }

    @Override
    public Response addPaperMetric(PaperMetricAddCmd cmd) {
        return  commandBus.send(cmd);
    }
}

看起来还挺干净的,可是 ATAMetricAddCmd 到底是被哪个 Executor 处理的呢,不直观。我还要去理解 CommandBus,以及 CommandBus 是如何注册 Executor 的。无形中增加了认知成本,不好。

既然这样,为何不用奥卡姆剃刀把这个 CommandBus 剔除呢。如下所示,去除 CommandBus 之后,代码是不是直观了很多,唯一的损失是我们会失去框架层面提供的 Interceptor 功能,然而,Interceptor 正是我下一个要动刀的地方。

public class MetricsServiceImpl implements MetricsServiceI{

    @Resource
    private ATAMetricAddCmdExe ataMetricAddCmdExe;
    @Resource
    private SharingMetricAddCmdExe sharingMetricAddCmdExe;
    @Resource
    private PatentMetricAddCmdExe patentMetricAddCmdExe;
    @Resource
    private PaperMetricAddCmdExe paperMetricAddCmdExe;

    @Override
    public Response addATAMetric(ATAMetricAddCmd cmd) {
        return ataMetricAddCmdExe.execute(cmd);
    }

    @Override
    public Response addSharingMetric(SharingMetricAddCmd cmd) {
        return sharingMetricAddCmdExe.execute(cmd);
    }

    @Override
    public Response addPatentMetric(PatentMetricAddCmd cmd) {
        return  patentMetricAddCmdExe.execute(cmd);
    }

    @Override
    public Response addPaperMetric(PaperMetricAddCmd cmd) {
        return  paperMetricAddCmdExe.execute(cmd);
    }
}

2. 去掉 Interceptor

当时设计 Interceptor,是因为有 CommandBus 作为基础,为了更好的利用命令模式带来的好处,便添加了 Interceptor 功能。其本质是一个 AOP 处理。

鉴于 Spring 的 AOP 功能已经很完善了,这个设计也是有点鸡肋。事实证明,大家在使用 COLA 框架的时候,很少会使用 Interceptor,包括我自己也是一样。既然如此,剔除也罢。

3. 去掉 Convertor、Validator、Assembler

关于命名的重要性,这里就不赘述了。当时想着是否能从框架层面,规范一下一些常用功能的命名。但是在实际使用中,发现这个想法也是有些过于理想化了。

我记得,在团队实践 COLA 的初期,还经常为什么是 Convertor(转换器),什么是 Assembler(组装器)的事情,争论不休。

后面我仔细想了想,命名虽然很重要,但其作用域最多也就是一个团队规范,你校验器是叫 Validator 还是 Checker 并没有什么本质区别,团队自己定义就好了。尝试从框架层面去解决团队约定问题,其效果不会太好,因此也果断挥刀剔除。

4. 类扫描优化

业务身份和扩展点的思想,是 TMF 的核心理念,也是阿里业务中台的进行多业务支持的核心方法论。

COLA 致力于提供一种轻量级的扩展实现方式,因此该功能在奥卡姆的屠刀下得以保存。因为 COLA 的扩展点设计是借鉴了中台的 TMF,因此在前面的设计中,其类扫描方案是直接照搬 TMF 的做法。

实际上,TMF 的类扫描方案对 COLA 来说有点多余。因为 COLA 本身就是架设在 Spring 的基础之上,而 Spring 又是建立在类扫描的基础之上。因此,我们完全可以复用 Spring 的类扫描,没必要自己写一套。

在原生的 Spring 中,至少有 3 种方式可以获取到用户自定义 Annotation 的 Bean,最简洁的是通过 ListableBeanFactory.getBeansWithAnnotation 方法,或者使用 ClassPathScanningCandidateComponentProvider 进行扫包。

在这次改版中,我选用的是 getBeansWithAnnotation 方法,主要是为了获取 @Extension 的 Bean,用来实现扩展点功能,废弃了原来的 TMF 类扫描实现。

总结

触发这次升级的动机,主要是因为,自己在实践 COLA 的过程中,的确发现有些华而不实的功能。在 COLA 作为阿里云的基础应用架构,其影响力越来越大的时候,我有责任给到大家一个正确的引导——去伪存真,简洁有效,而不是引入更多的复杂度。

实际上,COLA 是由两部分组成的:

一方面 COLA 是一种架构思想,是整合了洋葱圈架构、适配器架构、DDD、整洁架构、TMF 等架构思想的一种应用架构。

5.png

在这次升级中,架构思想部分基本没有变化,唯一一点是因为去除了 Command 概念,因此 CQRS 也成了可选项,而不再是一种强要求。

另一方面 COLA 也是框架组件,通过这次升级,我使用奥卡姆剃刀砍掉了绝大部分的组件能力,仅仅保留了扩展点功能。其用意是不希望 COLA 作为框架给到应用开发者太多的约束,这不符合简单有效的风格。

所以,总结下来,与其说这是一次升级,不如说它是功能“降级”,是在做减法。

但我相信,减法可以让 COLA 更加符合奥卡姆精神,帮助 COLA 轻装上阵,走的更远。

COLA 开源地址:https://github.com/alibaba/COLA

阿里云 JAVA 应用脚手架

start.aliyun.com 是基于 Spring-initializr 实现的工程脚手架生成平台,开发者们只需要添加一些注解和少量配置,就可以快速搭建分布式应用系统,它使用更亲切的中文,也不会有网络延迟问题,最重要的是提供更多本地化的组件依赖。

点击链接,立即体验阿里云 JAVA 应用脚手架:https://start.aliyun.com/?utm_content=g_1000150531

阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

原文链接
本文为云栖社区原创内容,未经允许不得转载。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

架构简洁之道:从阿里开源应用架构COLA说起 的相关文章

  • 云原生 AI 工程化实践之 FasterTransformer 加速 LLM 推理

    01 背景 OpenAI 在 3 月 15 日发布了备受瞩目的 GPT4 它在司法考试和程序编程领域的惊人表现让大家对大语言模型的热情达到了顶点 人们纷纷议论我们是否已经跨入通用人工智能的时代 与此同时 基于大语言模型的应用也如雨后春笋般出
  • ChaosBlade 项目指南:我是如何为社区贡献 Redis 故障场景

    01 Redis 新特性介绍 1 1 背景 Redis 实际使用过程中会存在一些故障演练需求 例如 模拟触发所有 key 过期的极端故障场景 模拟主动触发 Redis 内存淘汰策略释放内存场景等等 所以 根据以上故障演练需求 决定对 Cha
  • 金蝶管易云 X Hologres:新一代全渠道电商ERP最佳实践

    业务简介 金蝶管易云是金蝶集团旗下专注提供电商企业管理软件服务的子公司 成立于2008年 是国内最早的电商ERP服务商之一 目前已与300 主流电商平台建有合作关系 以企业数据为驱动 深度融合线上线下数据 为超过11万家客户提供实现业务 财
  • 阿里云:助力数字经济新基建,打造物联网安全基石

    5大安全产品全面升级 抢先了解 https developer aliyun com topic securityapril 预约观看发布会 https yq aliyun com live 2670 新基建大势之下的物联网 近日 中央政治
  • 迪士尼自研算法突破百万高清像素用AI换脸来拍电影

    AI换脸又一次刷爆了朋友圈 最近云毕业正当时 各家科技公司顺势推出了自己的AI换脸技术 结果又被同学们玩坏了 换脸这件事绝不能少了业界大佬们 不得不说 好看是不分性别的 彦宏兄气质满分 仔细来看 AI换脸技术近些年还是成熟不少 整体的面部贴
  • 闲置资源优化,轻松检查集群中的空闲成本

    前言 Kubernetes 提供了对计算 网络 存储资源的抽象 提升了集群资源管理的效率 然而 由于用户不需要直接管理底层资源 可能导致部分闲置资源未及时发现 造成成本浪费 在企业 IT 成本治理过程中 如何发现并处理这部分资源 是成本优化
  • 一次网络不通“争吵”引发的思考

    为啥争吵 吵什么 你到底在说什么啊 我K8s的ecs节点要访问clb的地址不通和本地网卡有什么关系 气愤语气都从电话那头传了过来 这时电话两端都沉默了 过了好一会传来地铁小姐姐甜美的播报声打断了刚刚的沉寂 乘坐地铁必须全程佩戴口罩 下一站西
  • 统一观测丨借助 Prometheus 监控 ClickHouse 数据库

    引言 ClickHouse 作为用于联机分析 OLAP 的列式数据库管理系统 DBMS 最核心的特点是极致压缩率和极速查询性能 同时 ClickHouse 支持 SQL 查询 在基于大宽表的聚合分析查询场景下展现出优异的性能 因此 获得了广
  • 本地 IDC 中的 K8s 集群如何以 Serverless 方式使用云上计算资源

    在前一篇文章 应对突发流量 如何快速为自建 K8s 添加云上弹性能力 中 我们介绍了如何为 IDC 中 K8s 集群添加云上节点 应对业务流量的增长 通过多级弹性调度 灵活使用云上资源 并通过自动弹性伸缩 提高使用率 降低云上成本 这种直接
  • Koordinator 异构资源/任务调度实践

    前言 Koordinator 是阿里云基于过去我们建设的统一调度系统中积累的技术和实践经验 对外开源了新一代的调度系统 Koordinator 支持 Kubernetes 上多种工作负载的混部调度 它的目标是提高工作负载的运行时效率和可靠性
  • 阿里云 MSE + ZadigX ,无门槛实现云原生全链路灰度发布

    企业发布现状痛点 目前企业在选择和实施发布策略时面临以下困境 1 缺乏云原生能力 由于从传统部署转变为云原生模式后 技术架构改造需要具备相关能力的人才 这使得企业在发布策略方面难以入手 2 缺乏自动化平台支持 即使找到适合产品现状的发布策略
  • 记一次容器环境下出现 Address not available

    困惑的源地址 pod 创建后一段时间一直是正常运行 突然有一天发现没有新的连接创建了 业务上是通过 pod A 访问 svc B 的 svc name 的方式 进入 pod 手动去 wget 一下 发现报错了 Address not ava
  • PolarDB-X 私有协议2.0

    本文主要介绍私有协议2 0 也即XRPC的背景 总体设计 相关技术实现细节和性能测试结果 私有协议作为解决 PolarDB X 中计算节点和存储节点复杂通信需求的技术手段 在 PolarDB X 2 0 公共云版上线初期就作为重要的功能一起
  • 架构简洁之道:从阿里开源应用架构COLA说起

    导读 COLA 的主要目的是为应用架构提供一套简单的可以复制 可以理解 可以落地 可以控制复杂性的 指导和约束 在实践中作者发现 COLA 在简洁性上仍有不足 因此给 COLA 做了一次 升级 在这次升级中 没有增加任何新的功能 而是尽量多
  • 快速玩转 Llama2!机器学习 PAI 最佳实践(三)—快速部署WebUI

    前言 近期 Meta 宣布大语言模型 Llama2 开源 包含7B 13B 70B不同尺寸 分别对应70亿 130亿 700亿参数量 并在每个规格下都有专门适配对话场景的优化模型Llama 2 Chat Llama2 可免费用于研究场景和商
  • SpringCloud Gateway 在微服务架构下的最佳实践

    前言 本文整理自云原生技术实践营广州站 Meetup 的分享 其中的经验来自于我们团队开发的阿里云 CSB 2 0 这款产品 其基于开源 SpringCloud Gateway 开发 在完全兼容开源用法的前提下 做了诸多企业级的改造 涉及功
  • 聊聊数据库中的 savepoint

    从全局二级索引讲起 故事要从全局二级索引开始讲起 当我们构建了一个全局二级索引之后 一条逻辑上的数据插入 就会变成两条物理上的数据插入 一条插入到主表 另一条插入到索引表 为了保证主表和索引表数据的一致性 我们往往需要开启分布式事务 再并行
  • 如何将个人 NAS 里的 Stable Diffusion 模型库挂载到 PAI-EAS

    通过在线迁移服务 您已经将SD公共模型库的模型文件转存到了自己的NAS文件目录中 该存储空间中的模型可以被用于SDWebUI 另外也可以将未来训练和推理的结果保存到该NAS目录中 您可以通过如下文件挂载方式来实现 1 前往文件存储NAS控制
  • 一文揭秘饿了么跨端技术的演进、实践与落地

    本文会先带领大家一起简单回顾下跨端技术背景与演进历程与在这一波儿接着一波儿的跨端浪潮中的饿了么跨端现状 以及在这个背景下 相较于业界基于 React Vue 研发习惯出发的各种跨端方案 饿了么为什么会选择走另外一条路 这个过程中我们的一些思
  • 快速玩转 Llama2!阿里云机器学习 PAI 推出最佳实践

    前言 近期 Meta 宣布大语言模型 Llama2 开源 包含7B 13B 70B不同尺寸 分别对应70亿 130亿 700亿参数量 并在每个规格下都有专门适配对话场景的优化模型Llama 2 Chat Llama2 可免费用于研究场景和商

随机推荐

  • 程序员微信名昵称_微信营销手段之昵称命名

    这段时间 我玩微信玩的不少 但是主要还是把精力放在我的QQ空间了 但是我不管在微信上还是扣扣空间我都发现了一个怪现象 就是一直有人用广告名称做昵称 啥是广告名字呢 比如她是卖化妆品的 然后她就把微信昵称改成了某某化妆品销售 代购什么的 这种
  • 史上最全Unity3D游戏开发教程,从入门到精通(含学习路线图)

    Unity现在已经用的很广泛啦 可是却一直没有什么美术向的教程 程序员方面的内容在各个论坛都有讨论 但是美术似乎很弱势啊 明明美术也很需要掌握引擎方面的内容嘛 山谷里的野百合还有春天呢 我们美术也要出教程 很多同学想学习unity却不知道怎
  • react中背景图片和图片引入的方法

    有三种引入背景图片的方法 1 div 2 先import引入图片路径 再用es6语法中的 引用 import bgImage from assets images bgImage webp div 3 用require进行路径引用requi
  • 【图像增强】Debiased Subjective Assessment of Real-World Image Enhancement

    最近学习了CVPR2021的一篇文章 真实世界图像增强的去偏主观质量评价 Debiased Subjective Assessment of Real World Image Enhancement 一 前言 图像质量评价 Image Qu
  • 【Keras】TensorFlow分布式训练

    当我们拥有大量计算资源时 通过使用合适的分布式策略 我们可以充分利用这些计算资源 从而大幅压缩模型训练的时间 针对不同的使用场景 TensorFlow 在 tf distribute Strategy 中为我们提供了若干种分布式策略 使得我
  • R语言相关关系可视化函数梳理

    点击蓝字关注这个神奇的公众号 作者 赵镇宁 R语言中文社区特约作者 当考察多个变量间的相关关系时 通常将多个变量的两两关系以矩阵的形式排列起来 R提供了散点图矩阵 相关矩阵等多种可视化方案 囊括了众多函数 本文对R语言相关关系可视化的函数进
  • iOS开发笔记--识别单击还是双击

    在视图上同时识别单击手势和双击手势的问题在于 当检测到一个单击操作时 无法确定是确实是一个单击操作或者只是双击操作中的第一次点击 解决这个问题的方法就是 在检测到单击时 需要等一段时间等待第二次点击 如果没有第二次点击 则为单击操作 如果有
  • spring 组件 扫描

    ComponentScan basePackages com 组件扫描 ComponentScan等价于 AnnotationConfigApplicationContext context new AnnotationConfigAppl
  • Web安全工具—nc(瑞士军刀)持续更新

    Web安全工具 nc 瑞士军刀 持续更新 提要 本文主要介绍NC工具的常用功能和原理 其他功能后续可在实际使用中进行学习和记录 简介 NC又被称为netcat 安全界成为瑞士军刀 其通常作用于渗透测试中信息收集和内网渗透阶段 主要功能 常用
  • GD32F103基础教程—外部中断实验(八)

    一 教程简介 本章主要是讲解GPIO输入实验 通过按键触发外部中断 控制LED2闪烁 二 实验流程 1 工程配置 外部中断触发实验工程配置方法与第五章的配置方法一致 具体请查看第五章教程 本章不再赘述 2 源码讲解 1 LED初始化 与之前
  • 使用Quartz2.2.3做持久化,启动程序后,控制台报错问题

    该错误是由mysql connector java jar版本太低导致 MLog clients using log4j logging Initializing c3p0 0 9 1 1 built 15 March 2007 01 32
  • 虚拟DOM中key的作用

    key是虚拟DOM的标识 如果数据发生变化时 Vue会根据 新数据 生成新虚拟DOM 和 旧虚拟DOM 的差异对比 如果旧虚拟DOM和新虚拟DOM的key值相同 并且虚拟DOM内容没有变 就直接使用原来的真实DOM 如果虚拟DOM中的内容变
  • PLC软元件2

    1 概述 上篇文章主要讲述了PLC中最基本的输入和输出软元件 同时在文章最后以最基本的输入和输出软元件完成一个比较基础的自锁和互锁功能 不太清楚的同学可以翻看上一篇文章 那PLC只有这些软元件么 其实远远不止 上次文章也讲了基本上MCU所具
  • ICCV 2023

    导读 TL DR 本文提出了FeatEnHancer 一种用于低光照视觉任务的增强型多尺度层次特征的新方法 提议的解决方案重点增强相关特征 通过提供强大的语义表示 使其优于现有的低光照图像增强方法 该方法不仅改进了单个特征的质量 而且还有效
  • ctfshow 文件包含

    目录 web78 web79 web80 81 web82 web83 web78 简单的伪协议文件包含 payload php filter convert base64 encode resource flag php web79 pa
  • npm install安装sharp包失败

    初次使用Lincm 使用后端koa框架初始化环境报错 npm config set sharp binary host https npm taobao org mirrors sharp npm config set sharp libv
  • Groovy与Java的不同点

    本文参考自Groovy文档 Differences with Java 所有代码都是Groovy文档中的 也可以将本文看做英文源文档的简略翻译 Groovy设计时目标之一就是让Java程序员快速习惯Groovy 不过在Groovy中也有很多
  • C#数据库MS SQL打开关闭演示

    书上记的比喻 有助于记忆 ADO NET类 CONNECTION对象好比伸入水中的水龙头 保持与水的接触 只有它与水进行了连接 其它对象才可以抽到水 COMMAND对象则像抽水机 为抽水提供动力和执行方法 先通过水龙头 然后把水返回给上面的
  • linux下rename函数用法,Linux学习之关于rename的用法

    linux下的rename是有两个版本的 一个是C语言版本 一个是Perl语言版本 如何判断当前是哪个版本 输入man rename 看到第一行是 RENAME 1 Linux Programmer s Manual RENAME 1 这个
  • 架构简洁之道:从阿里开源应用架构COLA说起

    导读 COLA 的主要目的是为应用架构提供一套简单的可以复制 可以理解 可以落地 可以控制复杂性的 指导和约束 在实践中作者发现 COLA 在简洁性上仍有不足 因此给 COLA 做了一次 升级 在这次升级中 没有增加任何新的功能 而是尽量多