在前面的《DDD 实战 (6):战略设计之技术决策》中,我曾经提到“微服务随时可拆可分”。而在上篇《DDD 实战(11):冲刺 1 代码 TDD 实现之道》几乎展示了所有 DDD 相关的、基于 TDD 代码“三部曲”的编程方式之后,就只上下这一个问题没有从代码角度进行演示了。本篇就来演示“微服务的随时可拆可分”这一 DDD 编程特性。同时,这将是本系列的最后一篇文章。

6. DDD 指导下的微服务实现

对于 DDD 方法体系下的微服务来说,其要回答的两个核心问题是:

  1. 如何在“北向网关”层向其它上下文提供合适的微服务输出,以便其客户端端口调用。

  2. 如何在“南向网关”层调用其它上下文的微服务。

下面的篇幅中,我将分别来进行说明。为了回答这两个问题,我们假设一个这样的“微服务拆分”应用场景:

  • 一开始,正如《DDD 实战 (6):战略设计之技术决策》中提到的那样,我是将“商品、接龙、订单”三个上下文的“命令”部分,作为一个微服务放到“业务处理中心”去的。

  • 但现在,假设考虑到“商品”上下文需求变更的频度极低(大概一季度不超过 1 次)、而“订单、接龙”上下文的需求则几乎每个月都要修改 10 多次,为此我们要将“商品、接龙、订单”三个上下文的“命令”部分拆分开来。这就会形成“商品中心”、“订单中心”、“接龙中心”3 个微服务。

  • 为了演示这一微服务过程是“随时可拆可合并”的,我们拿一个实际的例子,来看看如何作为不修改一行“商品、订单、接龙”领域核心(即:领域服务+聚合)的业务代码,而能够方便的进行拆分的。

  • 这个实际的例子就是:订单领域服务 OrderManagingService 在方法 submitOrder() 中进行订单提交时,将会通过南向网关 OrderItemsSettlementClient 端口的适配器调用商品上下文的领域服务 ProductSettlementService.calcSettlement()方法来获取商品结算价格。而在我们下面的示例代码中,将会看到如何不修改这两个领域服务(即 OrderManagingService 和 ProductSettlementService)的任何一行代码,实现了从原来的本地调用转换为远程服务调用。

注:本篇中所有代码,均可以在 gitee 或 github 代码库中下载:

  • gitee 代码库地址:https://gitee.com/samson-shu/starshop

  • github 代码库地址:https://github.com/beautautumn/starshop

6.1 定义远程服务公共接口

远程服务调用时,需要在“客户端”和“服务端”之间定义一个通用的 api 接口,这样才能方便客户端和服务端使用“同一种语言交谈”。为此,我们需要将 ProductSettlementService 这一领域服务的 calcSettlement() 方法进行“接口统一化”——事实上,是将 Product 命令上下文的应用服务方法、即 ProductAppService.calcSettlement()方法进行“接口统一化”。

6.1.1 定义统一接口

为此,我们在原有的工程目录下,新增一个 module,命名为 api,如下图:

然后,我们再新建一个“统一化接口”,该接口规定了商品价格结算服务的输入、输出。如下:

@FeignClient(value = "product-biz-services")public interface ProductBizService {     @PutMapping("/v2/products/settlements")     ProductSettlementResponse calcSettlement(@RequestBody ProductSettlementRequest request);}

复制代码

需要说明的是:为了支持 spring cloud 框架,我这里使用了 Feign。为此,我们需要在 api module 的 pom.xml 中引入相关依赖,如下:

<dependency>  <groupId>org.springframework.cloud</groupId>  <artifactId>spring-cloud-openfeign-core</artifactId>  <version>3.1.3</version></dependency>

复制代码

6.1.2 改造 pl 层 DTO 类

在统一接口 ProductBizService 的定义中,用到了商品价格结算请求类 ProductSettlementRequest、商品价格结算响应类 ProductSettlementResponse,而这两个类原来是商品上下文 pl 层的 DTO 类。为了能够实现商品上下文服务的“远程服务输出”,我们需要做如下的改造:

  • 将 ProductSettlementRequest 和 ProductSettlementResponse 从 product-command module 移动到 api module。

  • 将 ProductSettlementResponse 类中原有的工厂方法 from 独立到工厂类 ProductSettlementFactory 方法中去。之所以要将该工厂方法独立出来,是因为该工厂方法会引用到 Product 上下文的值对象类 ProductSettlement,也就是对 Product 上下文的“领域核心层”产生了依赖。为此,我们只能将该工厂方法独立出来,并仍然放到 Product 上下文的 pl 层中。

上面两个改造后,api 模块下的代码结构变成如下图:

ProductSettlementRequest DTO 类的代码如下:

@Data
@AllArgsConstructor
public class ProductSettlementRequest {private List<Long> productIds;private List<Integer> productCounts;public Map<LongIdentity, Integer> composeRequestToMap() {List<LongIdentity> ids = this.getProductIds().stream().map(LongIdentity::from).collect(Collectors.toList());Map<LongIdentity, Integer> productCountsMap = new HashMap<>();for (int i = 0; i < this.getProductIds().size(); i++) {productCountsMap.put(ids.get(i), this.getProductCounts().get(i));}return productCountsMap;}
}

ProductSettlementResponse DTO 类的代码如下:@

@Data
public class ProductSettlementResponse {@Getter@AllArgsConstructorpublic static class Item {private Long id;private String name;private Long priceFen;private Integer count;private BigDecimal quantity;private boolean available;private String productSnapshot;}private final List<Item> items = new ArrayList<>();}

product-command 模块改造后的独立工厂类所处代码结构如下:

工厂类 ProductSettlementFactory 的代码如下:

public class ProductSettlementFactory {public static ProductSettlementResponse settlementToResponse(Map<LongIdentity, ProductSettlement> settlements) {ProductSettlementResponse result = new ProductSettlementResponse();settlements.values().forEach(item -> result.getItems().add(ProductSettlementResponseItemMapper.INSTANCE.convert(item)));return result;}
}

6.2 商品上下文“北向网关”输出微服务

我们先来看商品上下文如何通过远程服务来输出微服务。为了更好的演示微服务常规实现方式,我这里演示了 Spring Cloud 和 dubbo 两种微服务实现。

6.2.1 Spring Cloud 北向网关远程服务输出

引入依赖

为了使用 spring cloud 框架,我们在 product 命令上下文的 pom.xml,即下图所示的 pom.xml 文件中引入相关依赖。

引入的依赖内容如下:

    <properties><eureka.version>3.1.3</eureka.version><spring-cloud.version>2021.0.1</spring-cloud.version></properties><dependencies><!-- spring cloud --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId><version>${eureka.version}</version></dependency><!--actuator monitor --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency></dependencies><dependencyManagement><!--springBoot的核心依赖,也是继承,可插拔机制,只会加载配置的依赖--><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>

输出 spring cloud 远程服务

事实上,我们并不需要修改原有的北向网关的本地服务(即应用服务),而是在远程服务中增加 ProductSpringCloudService 即可。

我们在 product-command 模块中增加 ProductSpringCloudService 类如下图所示的代码结构:

该类的代码内容如下(注意该服务实现了前面 api 模块定义的 PrductBizService 接口):

@RestController
@AllArgsConstructor
@RequestMapping("/v2/products")
public class ProductSpringCloudService implements ProductBizService {private final ProductAppService appService;@PutMapping("/settlements")public ProductSettlementResponse calcSettlement(@RequestBody ProductSettlementRequest request){return appService.calcSettlement(request);}}

可以看出:该远程服务只是实现了请求和响应的“透传”,直接调用本地应用服务 appservice 的 calcSettlement 方法,没有实现任何业务逻辑。

实现 spring cloud 微服务应用

微服务应用就是可以运行起来后作为微服务提供者的主程序。这个步骤在 spring boot 框架下特别简单,只需要新建一个 application,并使用相应的 annotation 注解即可。代码内容如下:@SpringBootApplication

@SpringBootApplication
@ComponentScan(basePackages = {"com.stardata.starshop2"})
@EnableDiscoveryClient
public class Starshop2ProductCommandSpringCloudApplication {public static void main(String[] args) {SpringApplication.run(Starshop2ProductCommandSpringCloudApplication.class, args);}
}

这里只是使用了 @EnableDiscoveryClient 这一注解指令而已,没有任何实质性代码。复制代码

另外,为了让 spring cloud 的应用作为微服务提供者运行,需要配置 application.properties 或 application.yml。配置内容如下(以 application.yml 为例):

eureka:#客户端client:# 注册中心地址service-url:defaultZone: http://localhost:7771/eureka/# 修改Eureka上的默认描述信息instance:instance-id: product-biz-center-8082
management:endpoints:web:exposure:include: "*"

其它说明

需要说明的是:spring cloud 还需要实现一个 Eureka Server 主程序,该实现过程特别简单,只需要新建一个模块,在 @SpringApplication 的注解基础上,引入 @EnableEurekaServer 注解即可(当然,包括 pom.xml 中需要引入相关依赖)。这里不再赘述。该主程序的代码如下:

@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {public static void main(String[] args) {SpringApplication.run(EurekaApplication.class, args);}
}

6.2.2 Dubbo 北向网关远程服务输出

实现 dubbo 微服务的输出,基本上和 spring cloud 的过程类似,无非还是:引入依赖、输出 dubbo 远程服务、实现 dubbo 微服务应用。具体说明如下:

引入依赖

引入 dubbo 相关依赖如下(由于 dubbo 项目本身包装的依赖内容与 spring cloud 依赖包有冲突,故这里是自行引入相关依赖包内容): <!-- dubbo -->

 <!-- dubbo --><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo</artifactId><version>${dubbo.version}</version></dependency><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-dependencies-zookeeper</artifactId><version>${dubbo.version}</version><type>pom</type><exclusions><exclusion><artifactId>slf4j-log4j12</artifactId><groupId>org.slf4j</groupId></exclusion><exclusion><artifactId>zookeeper</artifactId><groupId>org.apache.zookeeper</groupId></exclusion></exclusions></dependency><dependency><groupId>org.apache.curator</groupId><artifactId>curator-framework</artifactId><version>${curator-framework.version}</version></dependency><dependency><groupId>org.apache.curator</groupId><artifactId>curator-recipes</artifactId><version>${curator-recipes.version}</version><exclusions><exclusion><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId><version>${zookeeper.version}</version><exclusions><exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId></exclusion></exclusions></dependency><!-- dubbo starter --><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-spring-boot-starter</artifactId><version>${dubbo.version}</version></dependency>

输出 dubbo 远程服务

与前面的 spring cloud 类似,也是新增一个 remote 远程服务类 ProductDubboService。新增该远程服务后的代码结构图如下:

同样,ProductDubboService 类实现了 ProductBizService 接口,其代码如下:

@DubboService
@AllArgsConstructor
public class ProductDubboService implements ProductBizService {private final ProductAppService appService;public ProductSettlementResponse calcSettlement(ProductSettlementRequest request) {return appService.calcSettlement(request);}
}

注:该服务通过 @DubboService 注解实现了 dubbo 服务输出。同样,该远程服务只是协议透传,没有任何业务逻辑。

实现 dubbo 微服务应用

同样,dubbo 微服务需要有个主程序应用。其实现方式仍然是很简单,在 spring boot application 上加一个注解即可。代码如下:

@SpringBootApplication
@ComponentScan(basePackages = {"com.stardata.starshop2"})
@EnableDiscoveryClient
@EnableDubbo
public class Starshop2ProductCommandSpringCloudApplication {public static void main(String[] args) {SpringApplication.run(Starshop2ProductCommandSpringCloudApplication.class, args);}
}

可以看出:这里只是增加了 @EnableDubbo 注解而已。

6.3 订单上下文“南向网关”调用微服务

将“商品上下文”命令部分的代码部署成 spring cloud 或 dubbo 微服务提供者后,下面要做的工作,就是让订单上下文的领域服务 OrderManagingService.submitOrder() 方法在提交订单时,从原来本地方法调用(通过南向网关的 OrderItemsSettlementClientLocalAdapter 适配器)改成调用对应的微服务。

为此,我们需要做如下 3 方面的工作:

6.3.1 实现微服务应用

这其实和前面商品上下文实现微服务应用的做法类似,无非就是引入 spring cloud 或 dubbo 微服务框架(或任何其它微服务框架)相应的依赖。然后将 spring application 加上相应的注解后,将其转化为支持微服务框架的应用。

考虑到引入的 spring cloud 或 dubbo 依赖的内容与前面商品上下文相同,这里就不再赘述。改造后的订单上下文应用主程序代码如下:

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(basePackages = {"com.stardata.starshop2"})
@ComponentScan(basePackages = {"com.stardata.starshop2"})
@EnableDubbo
public class Starshop2OrderCommandApplication {public static void main(String[] args) {SpringApplication.run(Starshop2OrderCommandApplication.class, args);}
}

注意看:这里只是引入了 @EnableDiscoveryClient (支持 spring cloud 服务注册)、@EnableFeignClients(支持 spring cloud 客户端服务发现和负载均衡)、@EnableDubbo(支持 dubbo 服务注册)。

6.3.2 将南向网关适配器改为可配置模式

为了演示订单上下文调用商品上下文的端口 OrderItemsSettlementClient 的实现其实是可以“配置化”的——通过修改配置文件的方式来启用不同的适配器,我们对订单上下文端口 OrderItemsSettlementClient 实现“配置化改造”。

  • 首先,我们在应用配置文件 application.yml 中增加一个参数: adapter.orderItemsSettlement,该参数的配置示例如下:

adapter:#  orderItemsSettlement: orderItemsSettlementClientLocalAdapter#  orderItemsSettlement: orderItemsSettlementSpringCloudAdapter  orderItemsSettlement: orderItemsSettlementDubboAdapter

复制代码

从这里可以看出:这个适配器是可以随意通过配置文件来进行修改的。

  • 其次,我们在领域服务 OrderManagingService 的实现代码中,将适配器注入改造为“可配置”模式。改造后的 OrderManagingService 代码类似下图:

从红色框子可以看出,这里的 bean 注入变成了使用配置文件中参数名称作为 bean 名称的方式。事实上,我们甚至可以将这里所有的端口(如:预支付客户端端口 prepayingClient、订单资源库端口 orderRepository、参数资源库端口 parameterRepository 等),都通过配置文件来实现适配器的动态注入。

6.3.3 实现南向网关微服务远程调用

最后一步要做的工作,就是实现 spring cloud 或 dubbo 微服务框架下的、实现南向端口 OrderItemsSettlementClient 的适配器类。这两个适配器类所处的代码结构如下图:

这两个适配器的代码实现如下:

@Adapter(PortType.Client)
@Component("orderItemsSettlementSpringCloudAdapter")
@AllArgsConstructor
public class OrderItemsSettlementSpringCloudAdapter implements OrderItemsSettlementClient {private final ProductBizService productSettlementService;@Overridepublic void settleProducts(@NotNull Order order) {List<Long> productIds = new ArrayList<>();List<Integer> productCounts = new ArrayList<>();order.getItems().forEach(item -> {productIds.add(item.getProductId().value());productCounts.add(item.getPurchaseCount());});ProductSettlementRequest request = new ProductSettlementRequest(productIds, productCounts);ProductSettlementResponse response = productSettlementService.calcSettlement(request);for (ProductSettlementResponse.Item item : response.getItems()) {order.settleItem(LongIdentity.from(item.getId()), item.getName(), item.getCount(),item.getPriceFen(), item.getProductSnapshot());}}
}
@Adapter(PortType.Client)
@Component("orderItemsSettlementDubboAdapter")
@AllArgsConstructor
@Primary
public class OrderItemsSettlementDubboAdapter implements OrderItemsSettlementClient {@DubboReferenceprivate final ProductBizService productSettlementService;@Overridepublic void settleProducts(@NotNull Order order) {List<Long> productIds = new ArrayList<>();List<Integer> productCounts = new ArrayList<>();order.getItems().forEach(item -> {productIds.add(item.getProductId().value());productCounts.add(item.getPurchaseCount());});ProductSettlementRequest request = new ProductSettlementRequest(productIds, productCounts);ProductSettlementResponse response = productSettlementService.calcSettlement(request);for (ProductSettlementResponse.Item item : response.getItems()) {order.settleItem(LongIdentity.from(item.getId()), item.getName(), item.getCount(),item.getPriceFen(), item.getProductSnapshot());}}
}

我们再对比下原来本地调用的适配器类代码:

@Adapter(PortType.Client)
@Component("orderItemsSettlementClientLocalAdapter")
@AllArgsConstructor
public class OrderItemsSettlementClientLocalAdapter implements OrderItemsSettlementClient {private final ProductAppService productAppService;@Overridepublic void settleProducts(@NotNull Order order) {List<Long> productIds = new ArrayList<>();List<Integer> productCounts = new ArrayList<>();order.getItems().forEach(item -> {productIds.add(item.getProductId().value());productCounts.add(item.getPurchaseCount());});ProductSettlementRequest request = new ProductSettlementRequest(productIds, productCounts);ProductSettlementResponse response = productAppService.calcSettlement(request);for (ProductSettlementResponse.Item item : response.getItems()) {order.settleItem(LongIdentity.from(item.getId()), item.getName(), item.getCount(),item.getPriceFen(), item.getProductSnapshot());}}
}

可以看出,所修改的地方,无非就是:

  • 区别 1:使用的是本地 appService 调用、还是 dubbo 远程服务调用(通过 @DubboReference 注解引入)、或是 spring cloud 远程服务调用(通过 application 的 @EnableFeignClients 注解发现);

  • 区别 2: 给不同的 adapter 进行了不同的命名(使用 @Component 注解给出);

6.4 小结

通过如上的代码示例,我们可以看出“订单”和“商品上下文”的“领域核心”部分(指的是领域服务+实体类+值对象类)的代码逻辑,没有做哪怕一行的代码修改。所以,我们可以做出如下的几点小结:

  1. 当严格遵循“菱形架构”要求的 DDD 战术设计来实现代码后,微服务的拆分与否,其实完全不会影响 DDD 菱形架构下的“领域核心”部分的代码实现(包括:领域服务、实体和值对象);

  2. 为了很好的支持微服务的“分分合合”,建议将所有的上下文应用服务(也叫“本地服务”)的服务接口,都进行 api 公共服务接口的定义;

  3. 同时,为了很好的支持微服务的“分分合合”,也需要将所有上下文应用服务的出入参对应的 DTO 对象,也放到 api 公共服务接口的定义中去;

  4. 当然,当涉及到跨上下文的“数据库事务时”,建议通过“可靠消息总线”驱动下的“补偿交易 TCC”或“长时间事务 SAGA”来实现“最终事务一致性”,而不是“强事务一致性”。

  5. 当满足了以上几条后,微服务就可以做到“随时可拆可合”了。

事实上,我们也可以换句话说:只要 DDD 战略设计(限界上下文拆分与映射)、DDD 战术设计(遵循菱形架构)做得好,就完全可以在“代码开发层面上”完全不用关心“微服务拆分与否”这一问题,而将该问题完全转换成了系统运维层面上需要考虑的事情。

7. 结束语及倡议发起

本系列到这里就算全部结束了。希望本系列的文章,能够对您或您的团队在“如何将 DDD 实际落地应用”上有所帮助。

说实话,这一系列是我个人学习 DDD 过程中的一种记录,可能其中会有很多不完善之处。如果您发现我的 DDD 设计、或代码实现中,有任何不足之处,欢迎您随时加我微信: beautautumn,大家一起交流学习!

作为结束语,也作为我个人的一点私心,我在这里提出两个倡议:

7.1 倡议 1:在实践中使用

最近几年,DDD 在网络上有很多文章分享,很多程序员或架构师也都有很多不同的理解和应用,大家多多少少都存在着一定的“实践性”困难。

对此,我个人的建议是:不要停留在“书本知识”或“阅读他人文章”的层面,而是实实在在的拿着一个实际的项目、或者自己找一个练习性的项目(正如本文中做到的那样),来从头到尾把 DDD 方法体系用到的需求分析、战略设计、战术设计的内容进行实践,只有这样您才能真正深入的理解 DDD,而不仅仅是人云亦云、或人云非不云,那样的盲目遵从或盲目杠精其实都没有意义——相信我:作为软件架构师,从来没有一项软件架构的技能实践,不是在编程过程中、通过足够长的时间(一般半年以上)持续实践而积累起来的。

如果您认同我的观点:确实打算扎实掌握一门好的软件架构实践,而又实在找不到好的实践项目,我十分欢迎您参与我下面的一项倡议:共同打造一款 web3.0 模式下的生鲜供应链系统。

7.2 倡议 2:共同打造 web3.0 生鲜供应链系统

这个倡议,是出于我个人的私心:希望建立一个开源社区,打造一款 web3.0 模式下的、分布式应用和数据存储的生鲜供应链系统。

如果您有兴趣和耐心,且听我慢慢道来。。。

DDD 实战 (12- 终篇):DDD 下微服务的“分分合合”及一个倡议相关推荐

  1. 基于DDD(领域驱动设计)的微服务设计实例

    目录 一.战略设计: 1.产品愿景 2.场景分析 3.领域建模 1)提取领域对象 2)构建聚合 3)划分界限上下文 4.微服务拆分 二.战术设计 1.分析微服务领域对象 1)服务识别和设计 2)聚合内 ...

  2. 【Kubernetes 企业项目实战】07、最新一代微服务网格 Istio 入门到企业实战(下)

    目录 一.istio 核心资源解读 1.1 Gateway 总结: 1.2 VirtualService 1.2.1 VirtualService 主要由以下部分组成 1.2.2 virtualser ...

  3. 2020年,我来盘点下微服务架构技术栈

    2020年了,很多小伙伴儿对微服务还比较陌生,说起来很多人可能不敢相信,其实微服务这个概念早在2012年就提出来了,经过了这些年的发展,现在已经成为企业非常主流的架构选项了.今天,我就来带大家一起探讨 ...

  4. 又肝了下微服务 API 网关“金刚”,也是蛮香的~

    " 摘要: 原创出处 http://www.iocoder.cn/Kong/install/ 「芋道源码」欢迎转载,保留摘要,谢谢! 1. 概述 2. 快速安装 3. Kong 控制台 4. ...

  5. 如何构建成功的微服务架构?带你洞悉微服务构建流程,以实战角度出发,详解微服务架构

    前言 随着技术变得更加复杂,许多团队正在评估他们的架构如何最好地支持未来的业务.其中一种架构,微服务正在成为前瞻性技术部门越来越流行的选择.微服务架构可能是释放业务潜力的关键,但如何实现呢? 微服务是 ...

  6. SpringCloud(第 016 篇)电影微服务,定制Feign,一个Feign功能禁用Hystrix,另一个Feign功能启用Hystrix...

    2019独角兽企业重金招聘Python工程师标准>>> SpringCloud(第 016 篇)电影微服务,定制Feign,一个Feign功能禁用Hystrix,另一个Feign功能 ...

  7. 论文:Elastic Scheduling for Microservice Applications in Clouds (云环境下微服务应用的弹性调度)

    Elastic Scheduling for Microservice Applications in Clouds (云环境下微服务应用的弹性调度) 摘要: 微服务被广泛用于灵活的软件开发.最近,容 ...

  8. SpringCloud(第 017 篇)电影微服务接入Feign,添加 fallbackFactory 属性来触发请求进行容灾降级...

    2019独角兽企业重金招聘Python工程师标准>>> SpringCloud(第 017 篇)电影微服务接入Feign,添加 fallbackFactory 属性来触发请求进行容灾 ...

  9. 微服务架构讲解:那叫一个通俗易懂

    点击上方 Java后端,选择 设为星标 优质文章,及时送达 目录如下: 一.微服务架构介绍 二.出现和发展 三.传统开发模式和微服务的区别 四.微服务的具体特征 五.SOA和微服务的区别 六.如何具体 ...

最新文章

  1. DOS下导入导出MySQL备份
  2. Spring-注入参数详解-[简化配置方式]
  3. 设置Tomcat字符编码UTF-8
  4. Oracle RAC 客户端连接负载均衡(Load Balance)
  5. c# 创建委托 消息订阅_C#面向对象之委托和事件
  6. java后台获取流_java后台发送请求获取数据,并解析json数据
  7. 作者:曾琛(1987-),女,就职于中国科学院计算技术研究所。
  8. 第一章:Python的基本数据类型-第二节:Python中表示“有序”(序列)的数据类型
  9. Java 算法 身份证排序
  10. MyBatis(四)------MyBatis的生命周期及配置实例
  11. [ 数据集 ] VOC 2012 数据集介绍
  12. Air202学习(2)烧写底层固件
  13. 申请自己的免费企业邮箱
  14. 在线密码破解medusa
  15. 【verbs】ibv_query_qp()
  16. ping 计算机名判断机器是否在线,通过ping命令检测主机的存活性
  17. mqtt连接百度天工物接入平台
  18. CheatMaker教学进阶之一 - 基地址与指针
  19. MATLAB 数据分析方法(第2版)2.1 基本统计量与数据可视化
  20. gss1_GSS简介:网格样式表

热门文章

  1. 2021年制冷与空调设备安装修理考试题库及制冷与空调设备安装修理考试总结
  2. 蓝海卓越AP功能简述
  3. 多于两行文字隐藏并显示省略号
  4. Destroy 和 DestroyImmediate 的区别
  5. 为什么红黑树的最长路径不超过最短路径的两倍
  6. 45.qt quick-qml虚拟软键盘详解(一)
  7. 常见的6种数据分析图表,告诉你如何更好地分析它们
  8. 基于STC8G芯片的时钟显示系统
  9. android融云自定义通知,Android SDK 体系架构 - 融云 RongCloud
  10. tinder的定位在哪_我重新设计了Tinder。 这是我在此过程中学到的。