全链路自动化测试

2023-11-07

背景

从 SOA 架构到现在大行其道的微服务架构,系统越拆越小,整体架构的复杂度也是直线上升,我们一直老生常谈的微服务架构下的技术难点及解决方案也日渐成熟(包括典型的数据一致性,系统调用带来的一致性问题,还是跨节点跨机房复制带来的一致性问题都有了很多解决方案),但是有一个环节我们明显忽略了。

在现在的微服务架构趋势下,微服务在运维层面和自动化部署方面基本上是比较完善了。从我个人经验来看,上层的开发、测试对微服务架构带来的巨大变化还在反应和学习中。

开发层面讨论微服务的更多是框架、治理、性能等,但是从完整的软件工程来看我们严重缺失分析、设计知识,这也是我们现在的工程师普遍缺乏的技术。

我们经常会发现一旦你想重构点东西是多么的艰难,就是因为在初期构造这栋建筑的时候严重缺失了通盘的分析、设计,最终导致这个建筑慢慢僵化最后人见人怕,因为他逐渐变成一个怪物。(比如,开发很少写 unitTest ,我们总是忽视单元测试背后产生的软件工程的价值。)

被忽视的软件工程环节 — DEVTESTOPS

我们有没有发现一个现象,在整个软件过程里,测试这个环节容易被忽视。任何一种软件工程模型都有 QA 环节,但是这个环节似乎很薄很弱,目前我们绝大多数工程师、架构师都严重低估了这个环节的力量和价值,还停留在无技术含量,手动功能测试低级效率印象里。

这主要是测试这个角色整个技术体系、工程化能力偏弱,一部分是客观大环境问题,还有一部分自身问题,没有让自己走出去,多去学习整个工程化的技术,多去了解开发的技术,生产上的物理架构,这会有助于测试放大自己的声音。

导致测试环节在国内整个设计创新薄弱的原因还有一个主要原因就是,开发工程师普遍没有完整的工程基础。在国外IT发达国家,日本、美国等,一个合格的开发工程师、测试工程师都是边界模糊的,自己开发产品自己测试,这需要切换思维模式,需要同时具备这两种能力,但是这才是整个软件工程的完整流程。

我们有没有想过一个问题,为什么现在大家都在谈论 DevOps,而不是 DevTestOps,为什么偏偏跳过测试这个环节,难道开发的系统需要具备良好的可运维性就不需要可测试性吗,开发需要具备运维能力,运维需要具备开发能力,为什么测试环节忽略了。

我们对 QA 环节的轻视,对测试角色的不重视其实带来的副作用是非常大的。

微服务架构下测试复杂度和效率问题

微服务的拆分粒度要比 SOA 细了很多,从容器化镜像自动部署来衡量,是拆小了之后很方便,但是拆小了之后会给整个开发、测试环节增加很大的复杂度和效率问题。

在 SOA 时期,契约驱动 这个原则在微服务里也一样适用,跨部门需求定义好契约你就可以先开发上线了。但是这个里面最大的问题就是当前系统的部分连调问题和自动化回归问题,如果是新系统上线还需要做性能压测,这外部的依赖如何解决。

也许我们会说,不是应该依赖方先ready,然后我们紧接着进行测试、发布吗。如果是业务、架构合理的情况下,这种场景最大的问题就是我们的项目容易被依赖方牵制,这会带来很多问题,比如,研发人员需要切换出来做其他事情,branch 一直挂着,不知道哪天突然来找你说可以对接了,也许这已经过去一个月或者更久,这种方式一旦养成习惯性研发流程就很容易产生线上 BUG 。

还有一种情况也是合理的情况就是平台提供方需要调用业务方的接口,这里面有一般调用的 callback 接口、交易链路上的 marketing 接口、配送 routing 接口等。

这里给大家分享我们目前正在进行中的 marketing-cloud (营销云) 规则引擎 项目。

marketing-cloud 提供了一些营销类业务,有 团购优惠券促销 等,但是我们的业务方需要有自己个性化的营销活动玩法,我们需要在 marketing-cloud 规则引擎 中抽象出业务方营销活动的返回信息,同时打通个性化营销活动与公共交易、结算环节,形成一个完整的业务流。

这是一个 marketing-cloud 逻辑架构图,跟我们主题相关的就是 营销规则引擎 ,他就是我们这里所说的合理的业务场景。

在整个正向下单过程中,营销规则引擎要肩负起既要提供 marketing-cloud 内的共用营销活动,还需要桥接外部营销中心的各类营销玩法,外部的营销中心会有多个,目前我们主要有两个。

由于这篇文章不是介绍营销平台怎么设计,所以这里不打算扩展话题。主要是起到抛砖引玉的目的,平台型的业务会存在各种各样的对外系统依赖的业务场景。文章接下来的部分将展开 marketing-cloud 规则引擎 在打通测试链路上的实践。

开发阶段 unitTest mock 外部依赖

在开发阶段,我们会经常性的编写单元测试来测试我们的逻辑,在编写 unitTest 的时候都需要 mock 周边的依赖,mock 出来的对象分为两种类型,一种是不具有 Assert 逻辑的 stub 桩 对象,还有一种就是需要支持 Assert 的 mocker 模拟对象。

但是我们也不需要明显区分他们,两者的区别不是太明显,在编码规范内可能需要区分。

我们关心的是如何解决对象之间的依赖问题,各种 mock 框架其实提供了很多非常好用的工具,我们可以很轻松的 mock 周边的依赖。

given(marketingService.mixMarketingActivity(anyObject())).willReturn(stubResponse);
RuleCalculateResponse response = this.ruleCalculatorBiz.ruleCalculate(request);

这里我们 mock 了 marketingService.mixMarketingActivity() 方法。

Java 世界里提供了很多好用的 mock 框架,比较流行好用的框架之一 mockito 可以轻松 mock Service 层的依赖,当然除了 mockito 之外还有很多优秀的 mock 框架。

这些框架大同小异,编写 unitTest 最大的问题就是如何重构逻辑使之更加便于测试,也就是代码是否具备很好的可测试性,是否已经消除了绝大多数 private 方法,private 方法是否有某些指责是我们没有捕捉到业务概念。

连调阶段 mock 外部依赖

在我们完成了所有的开发,完善的单元测试保证了我们内部的逻辑是没有问题的(当然这里不讨论 unitTest 的 case 的设计是否完善情况)。

现在我们需要对接周边系统开发进行连调了,这个周边系统还是属于本平台之类的其他支撑系统。比如我们的 marketing-cloud 规则引擎系统 与 下单系统 之间的关系。在开发的时候我们编写 unitTest 是顺利的完成了开发解决的验证工作,但是现在面对连调问题。

系统需要正式的跑起来,但是我们缺乏对外部营销中心的依赖,我们怎么办。其实我们也需要在连调阶段 mock 外部依赖,只不过这个 mock 的技术和方法不是通过 unitTest 框架来支持,而是需要我们自己来设计我们的整个服务的开发架构。

首先要能识别本次 request 是需要 mock 的,那就需要某种 mock parameter 参数来提供识别能力。

我们来看下 marketing-cloud 营销规则引擎 在这块的一个初步尝试。

public interface CCMarketingCentralFacade {
    CallResponse callMarketingCentral(CallRequest request);
}
public interface ClassMarketingCentralFacade {
    CallResponse callMarketingCentral(CallRequest request);
}

营销规则引擎使用 RestEasy client api 作为 rest 调用框架。这两个 Facade 是营销平台对 CCTalk 、沪江网校 沪江两大子公司营销中心发起调用的 Facade

(为了尽量还原我们的工程实践干货同时需要消除一些敏感信息的情况下,整篇文章所有的代码实例,我都删除了一些不影响阅读且和本文无关的代码,同时做了一些伪编码和省略,使代码更精简更便于阅读。)

在正常逻辑下,我们会根据营销路由 key 来决定调用哪个公司的营销中心接口,但是由于我们在开发这个项目的时候暂时业务方还没有存在的地址让我们对接,所以我们自己做了 mock facade,来解决连调问题。

public class CCMarketingCentralFacadeMocker implements CCMarketingCentralFacade {

    @Override
    public CallResponse callMarketingCentral(CallRequest request) {

        CallResponse response = ...
        MarketingResultDto marketingResultDto = ...
        marketingResultDto.setTotalDiscount(new BigDecimal("90.19"));
        marketingResultDto.setUseTotalDiscount(true);

        response.getData().setMarketingResult(marketingResultDto);

        return response;
    }
}
public class ClassMarketingCentralFacadeMocker implements ClassMarketingCentralFacade {

    @Override
    public CallResponse callMarketingCentral(CallRequest request) {
        CallResponse response = ...

        MarketingResultDto marketingResultDto = ...
        marketingResultDto.setUseCoupon(true);
        marketingResultDto.setTotalDiscount(null);
        marketingResultDto.setUseTotalDiscount(false);

        List<MarketingProductDiscountDto> discountDtos = ...

        request.getMarketingProductTagsParameter().getMarketingTags().forEach(item -> {

            MarketingProductDiscountDto discountDto = ...
            discountDto.setProductId(item.getProductID());
            ...
            discountDtos.add(discountDto);
        });
...
        return response;
    }
}

我们定义了两个 mock 类,都是一些测试数据,就是为了解决在连调阶段的问题,也就是在 DEV 环境上的依赖问题。

有了 mock facade 之后就需要 request 定义 mock parameter 参数了。

public abstract class BaseRequest implements Serializable {
    public MockParameter mockParameter;
}
public class MockParameter {

    /**
     * mock cc 营销调用接口
     */
    public Boolean mockCCMarketingInterface;

    /**
     * mock class 营销调用接口
     */
    public Boolean mockClassMarketingInterface;

    /**
     * 是否自动化测试 mock
     */
    public Boolean useAutoTestMock;

    /**
     * 测试mock参数
     */
    public String testMockParam;

}

我们暂且忽略通用型之类的设计,这里只是我们在赶项目的情况下做的一个迭代尝试,等我们把这整个流程都跑通了再来考虑重构提取框架。

有了输入参数,我们就可以根据参数判断来动态注入 mock facade

自动化测试阶段 mock 需求

我们继续向前推进,过了连调阶段紧接着就进入测试环节,现在基本上大多数互联网公司都是自动化的测试,很少在有手动的,尤其是后端系统。

那么在 autoTest 阶段面临的一个问题就是,我们需要一个公共的 autoTest 地址,这个测试地址是不变的,我们在自动化测试下 mock 的 facade bean 的地址就是这个地址,这个地址输出的值需要能够对应到每次自动化脚本执行的上下文中。

我们有很多微服务系统来组成一个平台,每个服务都有依赖的第三方接口,原来在自动化测试这些服务的时候都需要去了解业务方系统的接口、DB、前台入口等,因为在编写自动化脚本的时候需要同步创建测试数据,最后才能 Assert

这个跨部门的沟通和协作效率严重低下,而且人员变动、系统变动都会直接影响上线周期,这里绝对值得创新来解决这个效率严重阻塞问题。

@Value("${marketing.cloud.business.access.url.mock}")
private String mockUrl;
/**
     * 自动化测试 mocker bean
     */
    @Bean("CCMarketingCentralFacadeTestMock")
    public CCMarketingCentralFacade CCMarketingCentralFacadeTestMock() {
        RestClientProxyFactoryBean<CCMarketingCentralFacade> restClientProxyFactoryBean ...
        restClientProxyFactoryBean.setBaseUri(this.mockUrl);
        ...
    }

    /**
     * 自动化测试 mocker bean
     */
    @Bean("ClassMarketingCentralFacadeTestMock")
    public ClassMarketingCentralFacade ClassMarketingCentralFacadeTestMock()  {
        RestClientProxyFactoryBean<ClassMarketingCentralFacade> restClientProxyFactoryBean ...
        restClientProxyFactoryBean.setBaseUri(this.mockUrl);
        ...
    }

这里的 mockUrl 就是我们抽象出来的统一的 autoTest 地址,在前面的 mock parameter 中有一个 useAutoTestMock Boolean 类型的参数,如果当前请求此参数为 true,我们将动态注入自动化测试 mock bean ,后续的所有调用都会走到 mockUrl 指定的地方。

autoTest Mock Gateway 浮出水面

到目前为止,我们遇到了自动化测试统一的 mock 地址要收口所有微服务在这方面的需求。现在最大的问题就是,所有的微服务对外依赖的 response 都不相同,自动化脚本在执行的时候预先创建好的 response 要能适配到当前测试的上下文中。

比如,营销规则引擎,我们的自动化脚本在创建一个订单的时候需要预先构造好当前商品(比如,productID:101010),在获取外部营销中心提供的活动信息和抵扣信息的 response ,最后才能去 Assert 订单的金额和活动信息记录是否正确,这就是一次 autoTest context 。

有两种方式来识别当前 autoTest context ,一种是在 case 执行的时候确定商品ID,最后通过商品ID来获取 mock 的 response 。还有一种就是支持传递 autoTest mock 参数给到 mockUrl 指定的服务,可以使用这个参数来识别当前测试上下文。

一个测试 case 可能会穿过很多微服务,这些所有的依赖服务可能都需要预设 mock response,这基本上是一劳永逸的。

所以,我们抽象出了 autoTest Mock Gateway(自动化测试mock网关服务) ,在整个自动化测试环节还有很多需要支持的工作,服务之间的鉴权,鉴权 key 的 mock,加解密,加解密 key 的 mock,自动化测试 case 交替并行执行等。

作为工程师的我们都希望用系统化、工程化的方式来解决整体问题,而不是个别点状问题。有了这个 mock gateway 我们可以做很多事情,也可以普惠所有需要的其他部门。

在一次 autoTest context 里构造好 mock response,然后通过 mock parameter 来动态识别具体的来源服务进行路由、鉴权、加解密等操作。

MockGateway 是一个支点,我相信这个支点可以撬动很多测试空间和创新能力。

轻量级版本实现

接下来我们将展示在 marketing-cloud 营销规则引擎 中的初步尝试。

整体逻辑架构

自动化脚本在每跑一个 case 的时候会创建当前 case 对应的 autoTestContext,这里面都是一些 meta data,用来表示这个 case 中所有涉及到的微服务系统哪些是需要走 mock gateway 的。

在 mockGateway 中所有的配置都是有一个 autoTestContext 所对应,如果没有 autoTestContext 说明是所有 case 共用。

将 mock parameter 纳入服务框架标准 request contract

要想打通整个微服务架构中的所有通道,就需要在标准 request contract 定义 mockParameter ,这是这一切的前提。

服务与服务之间调用走标准微服务 request contract,服务与外部系统的依赖可以选择走 HTTP Header,也可以选择走标准 request ,就要看我们的整个服务框架是否已经覆盖所有的产线及一些遗留系统的问题。

public abstract class BaseRequest implements Serializable {
    public MockParameter mockParameter;
}

BaseRequest 是所有 request 的基类,这样才能保证所有的请求能够正常的传递。

使用 AOP + RestEasy HttpClientRequest SPI 初步实现 Mock

整个系统的开发架构分层依赖是:facade->biz->service,基本的所有核心逻辑都是在 service 中,请求的 request dto 最多不能越界到 service 层,按照规范讲 request dto 顶多滞留在 biz 层,但是在互联网的世界中一些都是可以快速迭代的,并不是多么硬性规定,及时重构是偿还技术债务的主要方法。

前面我们已经讲过,我们采用的 RPC 框架是 RestEasy + RestEasy client ,我们先来看下入口的地方。

@Component
@Path("v1/calculator/")
public class RuleCalculatorFacadeImpl extends BaseFacade implements RuleCalculatorFacade {
    @MockFacade(Setting = MockFacade.SETTING_REQUEST_MOCK_PARAMETER)
    public RuleCalculateResponse ruleCalculate(RuleCalculateRequest request)  {
    ...
    }
}

再看下 service 对象。

@Component
public class MarketingServiceImpl extends MarketingBaseService implements MarketingService {
    @MockFacade(Setting = MockFacade.SETTING_FACADE_MOCK_BEAN)
    public MarketingResult onlyExtendMarketingActivity(Marketing..Parameter tagsParameter) {
    ...
    }

我们重点看下 @MockFacade annotation 声明。

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MockFacade {

    String SETTING_REQUEST_MOCK_PARAMETER = "setting_request_mock_parameter";
    String SETTING_FACADE_MOCK_BEAN = "setting_facade_mock_bean";

    String Setting();
}

通过这个 annotation 我们的主要目的就是将 mockParameter 放到 ThreadLocal 中去和请求处理完时的清理工作。还有一个功能就是 service 层的 mock bean 处理。

@Aspect
@Component
@Slf4j
public class MockMarketingFacadeInterceptor {

    @Before("@annotation(mockFacade)")
    public void beforeMethod(JoinPoint joinPoint, MockFacade mockFacade) {

        String settingName = mockFacade.Setting();

        if (MockFacade.SETTING_REQUEST_MOCK_PARAMETER.equals(settingName)) {

            Object[] args = joinPoint.getArgs();
            if (args == null) return;

            List<Object> argList = Arrays.asList(args);
            argList.forEach(item -> {

                if (item instanceof BaseRequest) {
                    BaseRequest request = (BaseRequest) item;

                    if (request.getMockParameter() != null) {
                        MarketingBaseService.mockParameterThreadLocal.set(request.getMockParameter());
                        log.info("----setting mock parameter:{}", JSON.toJSONString(request.getMockParameter()));
                    }
                }
            });
        } else if (MockFacade.SETTING_FACADE_MOCK_BEAN.equals(settingName)) {

            MarketingBaseService marketingBaseService = (MarketingBaseService) joinPoint.getThis();
            marketingBaseService.mockBean();
            log.info("----setting mock bean.");
        }
    }

    @After("@annotation(mockFacade)")
    public void afterMethod(JoinPoint joinpoint, MockFacade mockFacade) {

        if (MockFacade.SETTING_FACADE_MOCK_BEAN.equals(mockFacade.Setting())) {

            MarketingBaseService marketingBaseService = (MarketingBaseService) joinpoint.getThis();
            marketingBaseService.mockRemove();

            log.info("----remove mock bean.");
        }

        if (MockFacade.SETTING_REQUEST_MOCK_PARAMETER.equals(mockFacade.Setting())) {

            MarketingBaseService.mockParameterThreadLocal.remove();

            log.info("----remove ThreadLocal. ThreadLocal get {}", MarketingBaseService.mockParameterThreadLocal.get());
        }
    }
}

这些逻辑完全基于一个约定,就是 MarketingBaseService,不具有通用型,只是在逐步的重构和提取中,最终会是一个 plugin 框架。

public abstract class MarketingBaseService extends BaseService {

    protected ClassMarketingCentralFacade classMarketingCentralFacade;

    protected CCMarketingCentralFacade ccMarketingCentralFacade;

    public static ThreadLocal<MockParameter> mockParameterThreadLocal = new ThreadLocal<>();

    public void mockBean() {

        MockParameter mockParameter = mockParameterThreadLocal.get();

        if (mockParameter != null && mockParameter.mockClassMarketingInterface) {
            if (mockParameter.useAutoTestingMock) {
                this.setClassMarketingCentralFacade(SpringContextHolder.getBean("ClassMarketingCentralFacadeTestMock", ClassMarketingCentralFacade.class));
            } else {
                this.setClassMarketingCentralFacade(SpringContextHolder.getBean("ClassMarketingCentralFacadeMocker", ClassMarketingCentralFacadeMocker.class));
            }
        } else {
            this.setClassMarketingCentralFacade(SpringContextHolder.getBean("ClassMarketingCentralFacade", ClassMarketingCentralFacade.class));
        }

        if (mockParameter != null && mockParameter.mockCCMarketingInterface) {
            if (mockParameter.useAutoTestingMock) {
                this.setCcMarketingCentralFacade(SpringContextHolder.getBean("CCMarketingCentralFacadeTestMock", CCMarketingCentralFacade.class));
            } else {
                this.setCcMarketingCentralFacade(SpringContextHolder.getBean("CCMarketingCentralFacadeMocker", CCMarketingCentralFacadeMocker.class));
            }
        } else {
            this.setCcMarketingCentralFacade(SpringContextHolder.getBean("CCMarketingCentralFacade", CCMarketingCentralFacade.class));
        }
    }

    public void mockRemove() {
        mockParameterThreadLocal.remove();
    }
}

我们可以顺利的将 request 中的 mockParameter 放到 ThreadLocal 中,可以动态的通过 AOP 的方式来注入相应的 mockerBean

现在我们还要处理的就是对 mockGateway 的调用将 __mockParameter_ 中的 autoContext 中的标示字符串放到 HTTP Header 中去。

@Component
public class MockHttpHeadSetting implements ClientRequestFilter {

    @Override
    public void filter(ClientRequestContext requestContext) throws IOException {

        MultivaluedMap<String, Object> header = requestContext.getHeaders();

        MockParameter mockParameter = MarketingBaseService.mockParameterThreadLocal.get();

        if (mockParameter != null && StringUtils.isNotBlank(mockParameter.getTestingMockParam())) {
            header.add("Mock-parameter", mockParameter.getTestingMockParam());
        }
    }
}

接着在 SPI(javax.ws.rs.ext.Providers ) 文件中配置即可

com.hujiang.marketingcloud.ruleengine.service.MockHttpHeadSetting

总结

在整个微服务架构的实践中,工程界一直缺少探讨的就是在微服务架构的测试这块,离我们比较近的是自动化测试,因为自动化测试基本上是所有系统都需要的。

但是有一块我们一直没有重视的就是 全链路压力测试 这块,在生产上进行全链路的真实的压力测试需要解决很多问题,比较重要的就是 DB 这块,压测的时候产生的所有交易数据不能够参与结算、财务流程,这就需要借助 影子表 来解决,所有的数据都不会写入最终的真实的交易数据中去。当然还有其他地方都需要解决,一旦打开全链路压测开关,应该需要处理所有产生数据的地方,这是一个庞大的工程,但是也会非常有意思。

本篇文章只是我们在这块的一个初步尝试,我们会继续扩展下去,在下次产线全链路压测的时候我们就可以借助现在的实践架构扩展起来。

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

全链路自动化测试 的相关文章

  • 用于解析和构建逻辑表达式的 Java 库

    我正在寻找一个 Java 开源库来解析和构建类似 SQL 的表达式 例如评估表达式的有效性 例如 a x or y and b z 另外我想要一个用于构建或扩展表达式的 API 就像是 Expression exp new Expressi
  • 将构造函数作为参数传递给方法

    我是java新手 开始研究构造函数 我看到一些构造函数作为参数传递给方法的示例 请告诉我当构造函数作为参数传递给方法时会发生什么 或者建议我一些链接 我可以在其中获得有关使用构造函数的足够知识 根据您需要传递构造函数的目的 您可以考虑传递供
  • 使类只能从特定类实例化

    假设我有 3 节课class1 class2 and class3 我怎样才能拥有它class1只能通过实例化class2 class1 object new class1 但不是 class3 或任何其他类 我认为它应该与修饰符一起使用
  • 从 Spring MVC XML 文件转移到 javaconfig。我真的对我的数据库 XML 文件感到困惑

    我从 Spring MVC XML 文件转移到 javaconfig 我真的对我的数据库 XML 文件感到困惑 我不知道如何让 Hibernate4 工作以及我的 JBoss JNDI 数据源工作 有人可以告诉我如何使 javaconfig
  • 重写 getPreferredSize() 会破坏 LSP

    我总是在这个压倒一切的网站上看到建议getPreferredSize 而不是使用setPreferredSize 例如 如前面的线程所示 对于固定大小的组件 使用重写 getPreferredSize 而不是使用 setPreferredS
  • 我需要在 JFileChooser(打开模式)中显示不带扩展名的文件名。如何?

    我在打开模式下使用 JFileChooser 我需要显示不带扩展名的 文件名 字段 如何 我知道文件视图 它删除文件系统文件中的扩展名 但将所选文件中的扩展名保留在 文件名 字段中解释 http saveimg ru show image
  • 记录共享和映射的诊断上下文

    据我所知 其他人做了什么来解决 Commons Logging 项目 针对 NET 和 Java 不支持映射或嵌套诊断上下文这一事实 执行摘要 我们选择直接使用实现者日志框架 在我们的例子中为 log4j 长答案 您是否需要一个抽象日志框架
  • 如何在 MSSQL 中获取 CURRENT_DATE?

    我正在使用 jpa 3 o 和 Hibernate 我有一个命名查询 SELECT COUNT wt id FROM WPSTransaction wt WHERE wt createdDate gt CURRENT DATE WPSTra
  • 具有 JPA 持久性的 Spring 状态机 - 存储库使用

    我试图弄清楚如何轻松使用 Spring 状态机 包括使用 JPA 进行持久化 这是我正在处理的问题 不兼容的数据类型 工厂和持久性 在程序的某个时刻 我想使用连接到用户的状态机 有用于此目的的存储库 项目spring statemachin
  • 插入时的 iBatis 判别器

    我有一个抽象类Example以及与之相伴的具体子类 我使用鉴别器来提取数据out数据库的 像这样
  • 在 Java 中创建 XML 文件的最佳方法是什么?

    我们目前使用 dom4j 来创建 XML 文件 不过 我猜现在有更好的东西了 如果我们使用的是 Java 1 6 或更高版本 那么在编写 XML 文件时最好使用什么类 运行速度最快 使用简单 我不需要构建一个 DOM 然后编写整个 DOM
  • 使用单独的线程在java中读取和写入文件

    我创建了两个线程并修改了 run 函数 以便一个线程读取一行 另一个线程将同一行写入新文件 这种情况会发生直到整个文件被复制为止 我遇到的问题是 即使我使用变量来控制线程一一执行 但线程的执行仍然不均匀 即一个线程执行多次 然后控制权转移
  • 删除 JFX 中选项卡后面的灰色背景

    So is there any way to remove the gray area behind the tab s 我尝试过用 CSS 来做到这一点 但没有找到方法 要设置 tabpane 标题的背景颜色 请在 CSS 文件中写入 t
  • javax.media.jai 类的公共下载?

    这是一个非常简单的问题 我一直在寻找可以下载 javax media jai 库的地方 我找到了 jai imageio 库 但是我发现的所有其他 jai 内容要么已经过时 2008 年及之前 然后我遇到了登录屏幕 是否有 javax me
  • Android UnityPlayerActivity 操作栏

    我正在构建一个 Android 应用程序 其中包含 Unity 3d 交互体验 我已将 Unity 项目导入 Android Studio 但启动时该 Activity 是全屏的 并且不显示 Android 操作栏 我怎样才能做到这一点 整
  • 如何从 JavaFX 中的另一个控制器类访问 UI 元素?

    我有一个使用 NetBeans 8 编写的 JavaFX Java 8 应用程序 没有SceneBuilder 我的应用程序有一个主窗口 该窗口有自己的 FXML 文件 primary fxml 和自己的控制器类 FXMLPrimaryCo
  • Errors/BindingResult 参数应在模型属性、@RequestBody 或 @RequestPart 参数之后立即声明

    我通过剖析示例应用程序来自学 Spring 然后到处添加代码来测试我在剖析过程中开发的理论 在测试添加到 Spring 应用程序中的一些代码时 我收到以下错误消息 An Errors BindingResult argument is ex
  • 决策树和规则引擎 (Drools)

    In the application that I m working on right now I need to periodically check eligibility of tens of thousands of object
  • Java中单例的其他方式[重复]

    这个问题在这里已经有答案了 只是我在考虑编写单例类的其他方法 那么这个类是否被认为是单例类呢 public class MyClass static Myclass myclass static myclass new MyClass pr
  • mybatis:使用带有 XML 配置的映射器接口作为全局参数

    我喜欢使用 XML 表示法来指定全局参数 例如连接字符串 我也喜欢 Mapper 注释 当我尝试将两者结合起来时 我得到这个例外 https stackoverflow com questions 4263832 type interfac

随机推荐

  • 画图软件怎么做性能测试,软件性能测试能力提升解决方案.pdf

    软件性能测试能力提升解决方案 软件性能测试能力提升解决方案 软件性能测试能力提升解决方案 软软件件性性能能测测试试能能力力提提升升解解决决方方案案 课程试用 课程试用 课课程程试试用用 测试经理 TM 测试主测 TC 测试架构师 TAE 测
  • ChatGPT充值,银行卡被拒绝,图文教程

    目录 前言 步骤 1 魔法地址选择 2 选择手机号码 归属地 3 勾选 服从协议 4 填写信息 5 完善账单地址 6 订阅成功 前言 大家好 今天我在订阅ChatGPT4时 遭遇了银行卡被拒绝的尴尬境地 这里有个技巧 助你开心畅享ChatG
  • 大数据和人工智能的关系,超全解析

    大数据拥抱云计算 在PaaS层中一个复杂的通用应用就是大数据平台 大数据是如何一步一步融入云计算的呢 1数据不大也包含智慧 一开始这个大数据并不大 原来才有多少数据 现在大家都去看电子书 上网看新闻了 在我们80后小时候 信息量没有那么大
  • 使用C++封装MySQL API的教程(Python)

    在本教程中 我们将学习如何使用C 封装MySQL的API 并使用Python作为示例 我们将创建一个简单的程序 通过C 封装的MySQL API连接到MySQL数据库 并执行一些基本的数据库操作 MySQL是一个流行的开源关系型数据库管理系
  • ESP32(MicroPython)LVGL图形界面 RGB灯闪烁控制器

    ESP32 MicroPython RGB灯闪烁控制器 本程序通过依次调整RGB灯中每个灯的P 频率和占空比实现对RGB灯闪烁的控制 import lvgl as lv import time from espidf import VSPI
  • 【Shell牛客刷题系列】SHELL10 第二列是否有重复:复习sort命令和uniq命令~

    该系列是基于牛客Shell题库 针对具体题目进行查漏补缺 学习相应的命令 刷题链接 牛客题霸 Shell篇 该系列文章都放到专栏下 专栏链接为 专栏 Linux 欢迎关注专栏 本文知识预告 本文主要涉及的命令是sort命令和uniq命令 这
  • Python_inspect的使用

    The inspect module provides several useful functions to help get information about live objects such as modules classes
  • Kafka基础知识(个人总结)

    声明 1 本文为我的个人复习总结 并非那种从零基础开始普及知识 内容详细全面 言辞官方的文章 2 由于是个人总结 所以用最精简的话语来写文章 3 若有错误不当之处 请指出 消息队列 作用 优点 异步处理 使用微信 进行建行卡支付时 如果没资
  • 每日一题分享(三)

    给你一个含 n 个整数的数组 nums 其中 nums i 在区间 1 n 内 请你找出所有在 1 n 范围内但没有出现在 nums 中的数字 并以数组的形式返回结果 分析 第一种思路 1 我们可以新定义一个数组 里面放的就是1到n的数 2
  • (二十一)QT的构造函数重载

    在实际开发中 我们可能遇到一个问题 对于一个类 我们可能需要传递给它不同的参数 让它执行不同的行为 或者在使用旧的类时 我们希望加上一个新的参数 但是这个参数在旧的逻辑中不使用 需要在新的逻辑中使用 如果我们改了这个类 会导致使用旧的类的函
  • 打造高质量视频,创造视觉奇观!Camtasia 2023为你升级!

    嘿 伙计 在这个全新版本中 我们迎来了焕然一新的动画控制和更简化的特效制作流程 让创作变得更高效 不仅如此 全新的背景去除和动画光标功能也让视频拥有全新的视觉体验 让我们先谈谈光标 这个细节或许被忽视 却能让您的录屏更显个性 Camtasi
  • OSI七层网络结构图与TCP/IP五层网络结构图

    一 OSI七层网络结构图与TCP IP五层网络结构图 又称 OSI七层网络模型与TCP IP四层网络模型 1 OSI七层模型 OSI中的层 功能 TCP IP协议族 应用层 文件传输 电子邮件 文件服务 虚拟终端 TFTP HTTP SNM
  • storm计数器(小白看懂系列)

    现在要用storm做一个计数器 我的方案是 不断地输入一串字符串 然后统计每个单词的频数 这篇博客从以下几个方面进行阐述 基本配置 流程分析与类的确定 奉上代码 含注释 一 基本配置 这里注意 导包的时候要注意 否则可能会出现神奇的强制类型
  • websocket 接口如何测试?

    什么是 websocket 接口 使用 websocket 建立长连接 服务端和客户端可以互相通信 服务端只要有数据更新 就可以主动推给客户端 WebSocket 使得客户端和服务器之间的数据交换变得更加简单 允许服务端主动向客户端推送数据
  • 【深度学习】5:CNN卷积神经网络原理

    前言 先坦白的说 深度神经网络的学习在一开始对我造成的困扰还是很大的 我也是通过不断地看相关的视频资料 文献讲解尝试去理解记忆 毕竟这些内容大多都是不可查的 我们看到的都只是输入输出的东西 里面的内部运作以及工作原理 都需要沉心静思 这篇C
  • Python 远程控制Linux

    导入paramiko库 pip install paramiko import paramiko 创建SSHClient实例对象 ssh paramiko SSHClient 调用方法 表示没有存储远程机器的公钥 允许访问 ssh set
  • 使用flex布局 子元素高度自适应填满空白問題

    之前没用flex布局过高度自适应 倒腾了一下终于成功 但是还有个bug 必须要给父元素高度 不给高度就实现不了 来看下具体代码 由于子元素的图片大小不一样 要实现填满空白 要使用flex direction column 如果高度設置為he
  • 任意遥控器遥控C1测试成功,理论上可使用任意遥控

    转自 http bbs letv com thread 35538 1 1 html http bbs letv com thread 65155 1 1 html 任意遥控器遥控C1测试成功 理论上可使用任意遥控 现在测试了乐视随机九键遥
  • 源码安装openldap2.3以前版本

    http icephoenix us linuxunix build openldap 2 3 x from sources on ubuntu It happened so that I needed to build a particu
  • 全链路自动化测试

    背景 从 SOA 架构到现在大行其道的微服务架构 系统越拆越小 整体架构的复杂度也是直线上升 我们一直老生常谈的微服务架构下的技术难点及解决方案也日渐成熟 包括典型的数据一致性 系统调用带来的一致性问题 还是跨节点跨机房复制带来的一致性问题