本文档是《ASP.NET Core 迁移至 Spring系列:类库框架篇》的类库框架篇分篇。
版本基线
| 类别 | 版本 |
|---|---|
| C# | 12(.NET 8) |
| Java | 17(LTS) |
| Spring Boot | 3.2.x |
| ASP.NET Core | 8.0 |
| NuGet | 6.x |
| Maven | 3.9.x |
主流选型
| 维度 | 选型 | 备注 |
|---|---|---|
| Java 版本 | Java 17 LTS | --- |
| Web 框架 | Spring Boot 3.2.x | 默认 Tomcat |
| ORM | MyBatis-Plus | |
| 构建 | Maven | |
| 日志 | SLF4J + Logback | |
| 测试 | JUnit 5 | |
| 缓存 | Redis + Caffeine + Redisson | 二级缓存 + 分布式协调(锁/限流/集合) |
| MQ | RocketMQ / Kafka | 二选一 |
十、异步编程对照
10.1 C# async/await vs Java CompletableFuture
csharp
// C#
public async Task<string> FetchDataAsync(string url) {
var client = new HttpClient();
var response = await client.GetStringAsync(url);
return response;
}
// 调用
var data = await FetchDataAsync("https://api.com");
java
// Java
public CompletableFuture<String> fetchDataAsync(String url) {
HttpClient client = HttpClient.newHttpClient();
return client.sendAsync(
HttpRequest.newBuilder(URI.create(url)).build(),
HttpResponse.BodyHandlers.ofString()
).thenApply(HttpResponse::body);
}
// 调用
String data = fetchDataAsync("https://api.com").join();
10.2 异步模型对比
| 维度 | C# async/await | Java CompletableFuture | Java Reactive |
|---|---|---|---|
| 模型 | 状态机自动管理 | Future 链式调用 | 响应式流 |
| 错误处理 | try/catch | exceptionally / handle | onError |
| 组合 | await Task.WhenAll | allOf / anyOf | merge / zip |
| 取消 | CancellationToken | cancel(true) | dispose |
10.2.1 异步调用时序图
HTTP客户端 CompletableFuture 调用方 HTTP客户端 CompletableFuture 调用方 #mermaid-svg-LRso5u4w1QjZIYIJ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-LRso5u4w1QjZIYIJ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-LRso5u4w1QjZIYIJ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-LRso5u4w1QjZIYIJ .error-icon{fill:#552222;}#mermaid-svg-LRso5u4w1QjZIYIJ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-LRso5u4w1QjZIYIJ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-LRso5u4w1QjZIYIJ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-LRso5u4w1QjZIYIJ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-LRso5u4w1QjZIYIJ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-LRso5u4w1QjZIYIJ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-LRso5u4w1QjZIYIJ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-LRso5u4w1QjZIYIJ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-LRso5u4w1QjZIYIJ .marker.cross{stroke:#333333;}#mermaid-svg-LRso5u4w1QjZIYIJ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-LRso5u4w1QjZIYIJ p{margin:0;}#mermaid-svg-LRso5u4w1QjZIYIJ .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-LRso5u4w1QjZIYIJ text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-LRso5u4w1QjZIYIJ .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-LRso5u4w1QjZIYIJ .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-LRso5u4w1QjZIYIJ .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-LRso5u4w1QjZIYIJ .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-LRso5u4w1QjZIYIJ #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-LRso5u4w1QjZIYIJ .sequenceNumber{fill:white;}#mermaid-svg-LRso5u4w1QjZIYIJ #sequencenumber{fill:#333;}#mermaid-svg-LRso5u4w1QjZIYIJ #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-LRso5u4w1QjZIYIJ .messageText{fill:#333;stroke:none;}#mermaid-svg-LRso5u4w1QjZIYIJ .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-LRso5u4w1QjZIYIJ .labelText,#mermaid-svg-LRso5u4w1QjZIYIJ .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-LRso5u4w1QjZIYIJ .loopText,#mermaid-svg-LRso5u4w1QjZIYIJ .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-LRso5u4w1QjZIYIJ .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-LRso5u4w1QjZIYIJ .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-LRso5u4w1QjZIYIJ .noteText,#mermaid-svg-LRso5u4w1QjZIYIJ .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-LRso5u4w1QjZIYIJ .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-LRso5u4w1QjZIYIJ .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-LRso5u4w1QjZIYIJ .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-LRso5u4w1QjZIYIJ .actorPopupMenu{position:absolute;}#mermaid-svg-LRso5u4w1QjZIYIJ .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-LRso5u4w1QjZIYIJ .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-LRso5u4w1QjZIYIJ .actor-man circle,#mermaid-svg-LRso5u4w1QjZIYIJ line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-LRso5u4w1QjZIYIJ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} fetchDataAsync()sendAsync()完成回调thenApply结果
十一、依赖注入对照
11.1 数据结构
#mermaid-svg-pIgirK5uU0XowWgr{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-pIgirK5uU0XowWgr .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-pIgirK5uU0XowWgr .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-pIgirK5uU0XowWgr .error-icon{fill:#552222;}#mermaid-svg-pIgirK5uU0XowWgr .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-pIgirK5uU0XowWgr .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-pIgirK5uU0XowWgr .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-pIgirK5uU0XowWgr .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-pIgirK5uU0XowWgr .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-pIgirK5uU0XowWgr .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-pIgirK5uU0XowWgr .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-pIgirK5uU0XowWgr .marker{fill:#333333;stroke:#333333;}#mermaid-svg-pIgirK5uU0XowWgr .marker.cross{stroke:#333333;}#mermaid-svg-pIgirK5uU0XowWgr svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-pIgirK5uU0XowWgr p{margin:0;}#mermaid-svg-pIgirK5uU0XowWgr .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-pIgirK5uU0XowWgr .cluster-label text{fill:#333;}#mermaid-svg-pIgirK5uU0XowWgr .cluster-label span{color:#333;}#mermaid-svg-pIgirK5uU0XowWgr .cluster-label span p{background-color:transparent;}#mermaid-svg-pIgirK5uU0XowWgr .label text,#mermaid-svg-pIgirK5uU0XowWgr span{fill:#333;color:#333;}#mermaid-svg-pIgirK5uU0XowWgr .node rect,#mermaid-svg-pIgirK5uU0XowWgr .node circle,#mermaid-svg-pIgirK5uU0XowWgr .node ellipse,#mermaid-svg-pIgirK5uU0XowWgr .node polygon,#mermaid-svg-pIgirK5uU0XowWgr .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-pIgirK5uU0XowWgr .rough-node .label text,#mermaid-svg-pIgirK5uU0XowWgr .node .label text,#mermaid-svg-pIgirK5uU0XowWgr .image-shape .label,#mermaid-svg-pIgirK5uU0XowWgr .icon-shape .label{text-anchor:middle;}#mermaid-svg-pIgirK5uU0XowWgr .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-pIgirK5uU0XowWgr .rough-node .label,#mermaid-svg-pIgirK5uU0XowWgr .node .label,#mermaid-svg-pIgirK5uU0XowWgr .image-shape .label,#mermaid-svg-pIgirK5uU0XowWgr .icon-shape .label{text-align:center;}#mermaid-svg-pIgirK5uU0XowWgr .node.clickable{cursor:pointer;}#mermaid-svg-pIgirK5uU0XowWgr .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-pIgirK5uU0XowWgr .arrowheadPath{fill:#333333;}#mermaid-svg-pIgirK5uU0XowWgr .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-pIgirK5uU0XowWgr .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-pIgirK5uU0XowWgr .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pIgirK5uU0XowWgr .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-pIgirK5uU0XowWgr .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pIgirK5uU0XowWgr .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-pIgirK5uU0XowWgr .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-pIgirK5uU0XowWgr .cluster text{fill:#333;}#mermaid-svg-pIgirK5uU0XowWgr .cluster span{color:#333;}#mermaid-svg-pIgirK5uU0XowWgr div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-pIgirK5uU0XowWgr .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-pIgirK5uU0XowWgr rect.text{fill:none;stroke-width:0;}#mermaid-svg-pIgirK5uU0XowWgr .icon-shape,#mermaid-svg-pIgirK5uU0XowWgr .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pIgirK5uU0XowWgr .icon-shape p,#mermaid-svg-pIgirK5uU0XowWgr .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-pIgirK5uU0XowWgr .icon-shape .label rect,#mermaid-svg-pIgirK5uU0XowWgr .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pIgirK5uU0XowWgr .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-pIgirK5uU0XowWgr .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-pIgirK5uU0XowWgr :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 依赖注入容器
服务注册
服务解析
生命周期管理
Singleton
Scoped
Transient
ASP.NET Core DI
Spring IoC
Guice / Dagger
11.2 代码对照
csharp
// C# Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<IMyService, MyService>();
builder.Services.AddScoped<IRepository, Repository>();
builder.Services.AddTransient<IValidator, Validator>();
var app = builder.Build();
// 构造函数注入
public class MyController {
public MyController(IMyService service) { _service = service; }
}
java
// Java Spring
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
@Configuration
public class AppConfig {
// Spring 中无状态默认 bean 本身就是单例,不需要 @Singleton
// 若要显式标注,可使用 @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
@Bean
public MyService myService() { return new MyService(); }
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Validator validator() { return new Validator(); }
}
// 构造函数注入
@RestController
public class MyController {
private final MyService service;
public MyController(MyService service) { this.service = service; }
}
11.3 生命周期对照
重要说明 :ASP.NET Core 的
Scoped与 Spring 的request作用域语义近似但不完全相同 。ASP.NET Core 一个 HTTP 请求内 Scoped 实例复用;Spring 的request作用域只在 Web 容器内生效(需 RequestContextFilter),且Session作用域依赖 Session。当前表给出"近似关系",不是注解级一一对应。
| ASP.NET Core | Spring | 近似关系 | 说明 |
|---|---|---|---|
Singleton |
默认无状态 bean(@Scope("singleton")) |
强 | 容器内单例 |
Scoped |
@Scope("request") / @Scope("session") |
弱 | 请求/会话级,语义不完全相同 |
Transient |
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) |
强 | 每次注入新建 |
十二、配置文件对照
12.1 数据结构
#mermaid-svg-6auidQfvJcYbdh8j{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6auidQfvJcYbdh8j .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6auidQfvJcYbdh8j .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6auidQfvJcYbdh8j .error-icon{fill:#552222;}#mermaid-svg-6auidQfvJcYbdh8j .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6auidQfvJcYbdh8j .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6auidQfvJcYbdh8j .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6auidQfvJcYbdh8j .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6auidQfvJcYbdh8j .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6auidQfvJcYbdh8j .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6auidQfvJcYbdh8j .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6auidQfvJcYbdh8j .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6auidQfvJcYbdh8j .marker.cross{stroke:#333333;}#mermaid-svg-6auidQfvJcYbdh8j svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6auidQfvJcYbdh8j p{margin:0;}#mermaid-svg-6auidQfvJcYbdh8j .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-6auidQfvJcYbdh8j .cluster-label text{fill:#333;}#mermaid-svg-6auidQfvJcYbdh8j .cluster-label span{color:#333;}#mermaid-svg-6auidQfvJcYbdh8j .cluster-label span p{background-color:transparent;}#mermaid-svg-6auidQfvJcYbdh8j .label text,#mermaid-svg-6auidQfvJcYbdh8j span{fill:#333;color:#333;}#mermaid-svg-6auidQfvJcYbdh8j .node rect,#mermaid-svg-6auidQfvJcYbdh8j .node circle,#mermaid-svg-6auidQfvJcYbdh8j .node ellipse,#mermaid-svg-6auidQfvJcYbdh8j .node polygon,#mermaid-svg-6auidQfvJcYbdh8j .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6auidQfvJcYbdh8j .rough-node .label text,#mermaid-svg-6auidQfvJcYbdh8j .node .label text,#mermaid-svg-6auidQfvJcYbdh8j .image-shape .label,#mermaid-svg-6auidQfvJcYbdh8j .icon-shape .label{text-anchor:middle;}#mermaid-svg-6auidQfvJcYbdh8j .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6auidQfvJcYbdh8j .rough-node .label,#mermaid-svg-6auidQfvJcYbdh8j .node .label,#mermaid-svg-6auidQfvJcYbdh8j .image-shape .label,#mermaid-svg-6auidQfvJcYbdh8j .icon-shape .label{text-align:center;}#mermaid-svg-6auidQfvJcYbdh8j .node.clickable{cursor:pointer;}#mermaid-svg-6auidQfvJcYbdh8j .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6auidQfvJcYbdh8j .arrowheadPath{fill:#333333;}#mermaid-svg-6auidQfvJcYbdh8j .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6auidQfvJcYbdh8j .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6auidQfvJcYbdh8j .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6auidQfvJcYbdh8j .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6auidQfvJcYbdh8j .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6auidQfvJcYbdh8j .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6auidQfvJcYbdh8j .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6auidQfvJcYbdh8j .cluster text{fill:#333;}#mermaid-svg-6auidQfvJcYbdh8j .cluster span{color:#333;}#mermaid-svg-6auidQfvJcYbdh8j div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-6auidQfvJcYbdh8j .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6auidQfvJcYbdh8j rect.text{fill:none;stroke-width:0;}#mermaid-svg-6auidQfvJcYbdh8j .icon-shape,#mermaid-svg-6auidQfvJcYbdh8j .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6auidQfvJcYbdh8j .icon-shape p,#mermaid-svg-6auidQfvJcYbdh8j .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6auidQfvJcYbdh8j .icon-shape .label rect,#mermaid-svg-6auidQfvJcYbdh8j .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6auidQfvJcYbdh8j .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6auidQfvJcYbdh8j .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6auidQfvJcYbdh8j :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 配置文件
ASP.NET Core
Spring Boot
appsettings.json
appsettings.Development.json
IConfiguration
application.yml
application-dev.yml
@Value / @ConfigurationProperties
12.2 代码对照
json
// ASP.NET Core: appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information"
}
},
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=mydb"
}
}
yaml
# Spring Boot: application.yml
logging:
level:
root: INFO
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
12.3 读取配置
csharp
// C#
public class MyService(IConfiguration config) {
private string connStr = config.GetConnectionString("DefaultConnection");
}
java
// Java
@Service
public class MyService {
@Value("${spring.datasource.url}")
private String url;
}
十三、Web 框架对照
Spring Boot 默认 Tomcat,不是 Jetty 或 Netty 。
spring-boot-starter-web使用 Servlet 栈并默认装配 Tomcat;spring-boot-starter-webflux才是 Reactive 栈,默认装配 Reactor Netty。Jetty 和 Undertow 都需要显式切换,详见第三十三章。
13.1 数据结构
#mermaid-svg-PQW0O0m1gcFhMXIe{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-PQW0O0m1gcFhMXIe .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-PQW0O0m1gcFhMXIe .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-PQW0O0m1gcFhMXIe .error-icon{fill:#552222;}#mermaid-svg-PQW0O0m1gcFhMXIe .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PQW0O0m1gcFhMXIe .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-PQW0O0m1gcFhMXIe .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PQW0O0m1gcFhMXIe .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PQW0O0m1gcFhMXIe .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-PQW0O0m1gcFhMXIe .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PQW0O0m1gcFhMXIe .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PQW0O0m1gcFhMXIe .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PQW0O0m1gcFhMXIe .marker.cross{stroke:#333333;}#mermaid-svg-PQW0O0m1gcFhMXIe svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PQW0O0m1gcFhMXIe p{margin:0;}#mermaid-svg-PQW0O0m1gcFhMXIe .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-PQW0O0m1gcFhMXIe .cluster-label text{fill:#333;}#mermaid-svg-PQW0O0m1gcFhMXIe .cluster-label span{color:#333;}#mermaid-svg-PQW0O0m1gcFhMXIe .cluster-label span p{background-color:transparent;}#mermaid-svg-PQW0O0m1gcFhMXIe .label text,#mermaid-svg-PQW0O0m1gcFhMXIe span{fill:#333;color:#333;}#mermaid-svg-PQW0O0m1gcFhMXIe .node rect,#mermaid-svg-PQW0O0m1gcFhMXIe .node circle,#mermaid-svg-PQW0O0m1gcFhMXIe .node ellipse,#mermaid-svg-PQW0O0m1gcFhMXIe .node polygon,#mermaid-svg-PQW0O0m1gcFhMXIe .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-PQW0O0m1gcFhMXIe .rough-node .label text,#mermaid-svg-PQW0O0m1gcFhMXIe .node .label text,#mermaid-svg-PQW0O0m1gcFhMXIe .image-shape .label,#mermaid-svg-PQW0O0m1gcFhMXIe .icon-shape .label{text-anchor:middle;}#mermaid-svg-PQW0O0m1gcFhMXIe .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-PQW0O0m1gcFhMXIe .rough-node .label,#mermaid-svg-PQW0O0m1gcFhMXIe .node .label,#mermaid-svg-PQW0O0m1gcFhMXIe .image-shape .label,#mermaid-svg-PQW0O0m1gcFhMXIe .icon-shape .label{text-align:center;}#mermaid-svg-PQW0O0m1gcFhMXIe .node.clickable{cursor:pointer;}#mermaid-svg-PQW0O0m1gcFhMXIe .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-PQW0O0m1gcFhMXIe .arrowheadPath{fill:#333333;}#mermaid-svg-PQW0O0m1gcFhMXIe .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-PQW0O0m1gcFhMXIe .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-PQW0O0m1gcFhMXIe .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PQW0O0m1gcFhMXIe .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-PQW0O0m1gcFhMXIe .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PQW0O0m1gcFhMXIe .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-PQW0O0m1gcFhMXIe .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-PQW0O0m1gcFhMXIe .cluster text{fill:#333;}#mermaid-svg-PQW0O0m1gcFhMXIe .cluster span{color:#333;}#mermaid-svg-PQW0O0m1gcFhMXIe div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-PQW0O0m1gcFhMXIe .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-PQW0O0m1gcFhMXIe rect.text{fill:none;stroke-width:0;}#mermaid-svg-PQW0O0m1gcFhMXIe .icon-shape,#mermaid-svg-PQW0O0m1gcFhMXIe .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PQW0O0m1gcFhMXIe .icon-shape p,#mermaid-svg-PQW0O0m1gcFhMXIe .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-PQW0O0m1gcFhMXIe .icon-shape .label rect,#mermaid-svg-PQW0O0m1gcFhMXIe .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PQW0O0m1gcFhMXIe .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-PQW0O0m1gcFhMXIe .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-PQW0O0m1gcFhMXIe :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Web 框架
ASP.NET Core MVC
Spring Boot MVC
Controller
Action
Filter
Middleware
@RestController
@GetMapping/@PostMapping
Filter / Interceptor
HandlerInterceptor
13.2 Controller 对照
csharp
// C# ASP.NET Core
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase {
private readonly IUserService _userService;
public UserController(IUserService userService) {
_userService = userService;
}
[HttpGet("{id}")]
public async Task<ActionResult<User>> GetUser(int id) {
var user = await _userService.GetByIdAsync(id);
return user == null ? NotFound() : Ok(user);
}
[HttpPost]
public async Task<ActionResult<User>> CreateUser(User user) {
var created = await _userService.CreateAsync(user);
return CreatedAtAction(nameof(GetUser), new { id = created.Id }, created);
}
}
java
// Java Spring Boot
@RestController
@RequestMapping("/api/user")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable int id) {
User user = userService.getById(id);
return user == null ? ResponseEntity.notFound().build() : ResponseEntity.ok(user);
}
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User created = userService.create(user);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}
}
13.2.1 Web 请求处理时序图
数据库 Repository Service DI 容器 Controller Middleware/Filter 链 Kestrel/Tomcat 客户端 数据库 Repository Service DI 容器 Controller Middleware/Filter 链 Kestrel/Tomcat 客户端 #mermaid-svg-NfDQ5lICV39mbwHi{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-NfDQ5lICV39mbwHi .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NfDQ5lICV39mbwHi .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NfDQ5lICV39mbwHi .error-icon{fill:#552222;}#mermaid-svg-NfDQ5lICV39mbwHi .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NfDQ5lICV39mbwHi .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NfDQ5lICV39mbwHi .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NfDQ5lICV39mbwHi .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NfDQ5lICV39mbwHi .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NfDQ5lICV39mbwHi .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NfDQ5lICV39mbwHi .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NfDQ5lICV39mbwHi .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NfDQ5lICV39mbwHi .marker.cross{stroke:#333333;}#mermaid-svg-NfDQ5lICV39mbwHi svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NfDQ5lICV39mbwHi p{margin:0;}#mermaid-svg-NfDQ5lICV39mbwHi .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-NfDQ5lICV39mbwHi text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-NfDQ5lICV39mbwHi .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-NfDQ5lICV39mbwHi .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-NfDQ5lICV39mbwHi .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-NfDQ5lICV39mbwHi .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-NfDQ5lICV39mbwHi #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-NfDQ5lICV39mbwHi .sequenceNumber{fill:white;}#mermaid-svg-NfDQ5lICV39mbwHi #sequencenumber{fill:#333;}#mermaid-svg-NfDQ5lICV39mbwHi #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-NfDQ5lICV39mbwHi .messageText{fill:#333;stroke:none;}#mermaid-svg-NfDQ5lICV39mbwHi .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-NfDQ5lICV39mbwHi .labelText,#mermaid-svg-NfDQ5lICV39mbwHi .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-NfDQ5lICV39mbwHi .loopText,#mermaid-svg-NfDQ5lICV39mbwHi .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-NfDQ5lICV39mbwHi .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-NfDQ5lICV39mbwHi .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-NfDQ5lICV39mbwHi .noteText,#mermaid-svg-NfDQ5lICV39mbwHi .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-NfDQ5lICV39mbwHi .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-NfDQ5lICV39mbwHi .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-NfDQ5lICV39mbwHi .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-NfDQ5lICV39mbwHi .actorPopupMenu{position:absolute;}#mermaid-svg-NfDQ5lICV39mbwHi .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-NfDQ5lICV39mbwHi .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-NfDQ5lICV39mbwHi .actor-man circle,#mermaid-svg-NfDQ5lICV39mbwHi line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-NfDQ5lICV39mbwHi :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} HTTP 请求1进入中间件链2路由匹配 + 鉴权3注入 Service4返回 Service 实例5调用业务方法6数据操作7SQL8结果集9实体10业务结果11ResponseEntity12HTTP 响应(JSON)13
13.3 注解对照
| ASP.NET Core | Spring MVC | 说明 |
|---|---|---|
[ApiController] |
@RestController + 参数校验/异常处理配置 |
功能相近但非注解级一一对应 |
[Route] |
@RequestMapping |
路由映射 |
[HttpGet] |
@GetMapping |
GET 请求 |
[HttpPost] |
@PostMapping |
POST 请求 |
[HttpPut] |
@PutMapping |
PUT 请求 |
[HttpDelete] |
@DeleteMapping |
DELETE 请求 |
[FromBody] |
@RequestBody |
请求体 |
[FromQuery] |
@RequestParam |
查询参数 |
[FromRoute] |
@PathVariable |
路径参数 |
[FromHeader] |
@RequestHeader |
请求头 |
[Authorize] |
@PreAuthorize |
权限校验 |
十四、ORM 对照(主流选型:MyBatis-Plus)
14.1 MyBatis-Plus 是什么
MyBatis-Plus 是 MyBatis 的增强工具,在保留 SQL 可见性和可控性的基础上,补齐通用 CRUD、条件构造器、分页、逻辑删除、乐观锁和代码生成能力。对 ASP.NET Core 人员来说,它更接近"EF Core 的仓储效率 + 显式 SQL 的控制力",而不是把数据库行为完全藏起来的全自动 ORM。
主线只需要先记住四个入口:BaseMapper 提供 Mapper 层通用 CRUD,IService / ServiceImpl 提供 Service 层增强,QueryWrapper / LambdaQueryWrapper 构造条件,MybatisPlusInterceptor 挂载分页、租户和数据权限等插件。
14.2 C# EF Core 对照 MyBatis-Plus
| 维度 | EF Core | MyBatis-Plus |
|---|---|---|
| 数据访问模型 | DbContext + DbSet<T> |
BaseMapper<T> + IService<T> |
| 查询方式 | LINQ 表达式由框架翻译为 SQL | Wrapper、注解或 XML 显式组织 SQL |
| SQL 可见性 | 通过日志和拦截器观察生成 SQL | 自定义 SQL 与执行计划更直接可见 |
| 实体映射 | Fluent API / Data Annotations | @TableName、@TableId、@TableField |
| 通用 CRUD | Add、Update、Remove、SaveChanges |
insert、updateById、deleteById、selectById |
| 分页 | LINQ Skip / Take 或扩展库 |
PaginationInnerInterceptor + IPage |
| 批量 / 事务 | SaveChanges + TransactionScope |
Service 方法 + Spring @Transactional |
| 迁移时的注意点 | 表达式、跟踪状态和连接生命周期 | SQL、结果映射、事务边界和分页 count 由开发者关注 |
14.3 MyBatis-Plus 核心组件
#mermaid-svg-3wcSuOZeY4B9NeHn{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3wcSuOZeY4B9NeHn .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3wcSuOZeY4B9NeHn .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3wcSuOZeY4B9NeHn .error-icon{fill:#552222;}#mermaid-svg-3wcSuOZeY4B9NeHn .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3wcSuOZeY4B9NeHn .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3wcSuOZeY4B9NeHn .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3wcSuOZeY4B9NeHn .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3wcSuOZeY4B9NeHn .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3wcSuOZeY4B9NeHn .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3wcSuOZeY4B9NeHn .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3wcSuOZeY4B9NeHn .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3wcSuOZeY4B9NeHn .marker.cross{stroke:#333333;}#mermaid-svg-3wcSuOZeY4B9NeHn svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3wcSuOZeY4B9NeHn p{margin:0;}#mermaid-svg-3wcSuOZeY4B9NeHn .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3wcSuOZeY4B9NeHn .cluster-label text{fill:#333;}#mermaid-svg-3wcSuOZeY4B9NeHn .cluster-label span{color:#333;}#mermaid-svg-3wcSuOZeY4B9NeHn .cluster-label span p{background-color:transparent;}#mermaid-svg-3wcSuOZeY4B9NeHn .label text,#mermaid-svg-3wcSuOZeY4B9NeHn span{fill:#333;color:#333;}#mermaid-svg-3wcSuOZeY4B9NeHn .node rect,#mermaid-svg-3wcSuOZeY4B9NeHn .node circle,#mermaid-svg-3wcSuOZeY4B9NeHn .node ellipse,#mermaid-svg-3wcSuOZeY4B9NeHn .node polygon,#mermaid-svg-3wcSuOZeY4B9NeHn .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3wcSuOZeY4B9NeHn .rough-node .label text,#mermaid-svg-3wcSuOZeY4B9NeHn .node .label text,#mermaid-svg-3wcSuOZeY4B9NeHn .image-shape .label,#mermaid-svg-3wcSuOZeY4B9NeHn .icon-shape .label{text-anchor:middle;}#mermaid-svg-3wcSuOZeY4B9NeHn .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3wcSuOZeY4B9NeHn .rough-node .label,#mermaid-svg-3wcSuOZeY4B9NeHn .node .label,#mermaid-svg-3wcSuOZeY4B9NeHn .image-shape .label,#mermaid-svg-3wcSuOZeY4B9NeHn .icon-shape .label{text-align:center;}#mermaid-svg-3wcSuOZeY4B9NeHn .node.clickable{cursor:pointer;}#mermaid-svg-3wcSuOZeY4B9NeHn .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3wcSuOZeY4B9NeHn .arrowheadPath{fill:#333333;}#mermaid-svg-3wcSuOZeY4B9NeHn .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3wcSuOZeY4B9NeHn .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3wcSuOZeY4B9NeHn .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3wcSuOZeY4B9NeHn .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3wcSuOZeY4B9NeHn .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3wcSuOZeY4B9NeHn .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3wcSuOZeY4B9NeHn .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3wcSuOZeY4B9NeHn .cluster text{fill:#333;}#mermaid-svg-3wcSuOZeY4B9NeHn .cluster span{color:#333;}#mermaid-svg-3wcSuOZeY4B9NeHn div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3wcSuOZeY4B9NeHn .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3wcSuOZeY4B9NeHn rect.text{fill:none;stroke-width:0;}#mermaid-svg-3wcSuOZeY4B9NeHn .icon-shape,#mermaid-svg-3wcSuOZeY4B9NeHn .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3wcSuOZeY4B9NeHn .icon-shape p,#mermaid-svg-3wcSuOZeY4B9NeHn .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3wcSuOZeY4B9NeHn .icon-shape .label rect,#mermaid-svg-3wcSuOZeY4B9NeHn .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3wcSuOZeY4B9NeHn .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3wcSuOZeY4B9NeHn .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3wcSuOZeY4B9NeHn :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} MyBatis-Plus
BaseMapper
IService / ServiceImpl
Wrapper 条件构造器
MybatisPlusInterceptor
注解映射
代码生成器
通用 CRUD
业务服务复用
QueryWrapper
LambdaQueryWrapper
分页
多租户 / 数据权限
@TableName / @TableId
AutoGenerator
| 组件 | 作用 | 迁移时对应理解 |
|---|---|---|
BaseMapper<T> |
提供单表常用 CRUD | 省掉大量 Repository 样板代码 |
IService<T> / ServiceImpl |
在 Service 层复用 CRUD、批量和分页能力 | 业务服务仍应保留事务和领域规则 |
QueryWrapper<T> |
用字符串列名拼接动态条件 | 灵活,但列名重构缺少类型检查 |
LambdaQueryWrapper<T> |
用方法引用构造条件 | 常规查询优先使用,重构更安全 |
MybatisPlusInterceptor |
注册分页、租户、数据权限等插件 | 统一放在 Spring 配置中管理 |
@TableName / @TableId / @TableField |
声明表名、主键和字段映射 | 对应 EF Core 的实体映射配置 |
14.4 上手第一步:在 Spring Boot 17 项目中集成 MyBatis-Plus
把最小闭环压缩成三步:加依赖、配置数据源、写实体和接口。示例使用 MySQL 和逻辑删除字段;生产项目应把密码放在环境变量或密钥管理服务中。
xml
<!-- 1. 加依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.9</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
yaml
# 2. application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/demo
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
logic-delete-field: deleted
id-type: auto
java
// 3. 写代码
@Data
@TableName("t_user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
}
public interface UserMapper extends BaseMapper<User> {}
public interface UserService extends IService<User> {}
@Service
@AllArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
implements UserService {
public boolean saveUser(User user) { return save(user); }
}
@RestController
@RequestMapping("/api/user")
@AllArgsConstructor
public class UserController {
private final UserService userService;
@GetMapping("/{id}")
public User getById(@PathVariable Long id) {
return userService.getById(id);
}
}
跑起来:mvn spring-boot:run
示例中的
@Data、@AllArgsConstructor来自 Lombok;如果项目没有统一引入 Lombok,改成显式 getter、setter 和构造器即可。StdOutImpl适合本地确认 SQL,生产环境继续使用 SLF4J + Logback 的日志配置。
14.5 Wrapper 条件构造器
java
// QueryWrapper(字符串列名,适合动态拼接但重构时缺少类型检查)
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name", "张三")
.ge("age", 18)
.like("email", "@gmail.com")
.orderByDesc("create_time");
List<User> users = userMapper.selectList(wrapper);
// LambdaQueryWrapper(类型安全,推荐)
LambdaQueryWrapper<User> lambda = new LambdaQueryWrapper<>();
lambda.eq(User::getName, "张三")
.ge(User::getAge, 18)
.like(User::getEmail, "@gmail.com")
.orderByDesc(User::getCreateTime);
List<User> users = userMapper.selectList(lambda);
LambdaQueryWrapper 解决的是 Java 属性重命名时的编译期约束,不会替开发者检查索引、分页规模或 SQL 执行计划;复杂查询仍应回到 SQL 和数据库指标本身。
14.6 FAQ
- Q1:MyBatis-Plus 与 JPA / Hibernate 如何选型?
- 按本指南的新项目口径直接使用 MyBatis-Plus;接手老项目时先识别现有持久化边界,不要为了换框架而重写稳定查询。
- Q2:MyBatis-Plus 分页插件原理?
PaginationInnerInterceptor根据数据库类型改写分页 SQL,并生成 count 查询后封装为IPage;大表要关注 count 成本、索引和深分页。
- Q3:MyBatis-Plus 乐观锁如何实现?
- 实体增加
@Version字段并注册OptimisticLockerInnerInterceptor;更新受影响行数为 0 时由业务决定重试、提示或放弃。
- 实体增加
- Q4:LambdaQueryWrapper 与 QueryWrapper 区别?
QueryWrapper使用字符串列名,动态性高但重构不安全;LambdaQueryWrapper使用方法引用,常规 Java 查询优先使用后者。
- Q5:MyBatis-Plus 与 Spring Data JPA 性能如何比较?
- 没有脱离查询、索引、连接池、返回列数和数据量的固定结论;应固定数据集和并发模型,比较执行计划、P95/P99、数据库 CPU、网络传输量和 GC。
- Q6:在 Spring Boot 17 项目中如何选择 MyBatis-Plus vs Spring Data JPA?
- 新项目按本指南统一 MyBatis-Plus;只有在接手已有 JPA 领域模型且改造收益明确时才继续沿用,避免同一聚合混用两套持久化语义。
14.7 老项目速览(一句话带过)
| 老方案 | 现在还用吗 | 应对 |
|---|---|---|
| Spring Data JPA / Hibernate | 老项目 / 金融行业仍有 | 看一眼 JpaRepository 即可 |
| MyBatis 原生 | 部分国产老项目 | 用 @Mapper 注解,无 BaseMapper |
| JOOQ / Exposed | 几乎不用 | 跳过 |
十五、日志对照
15.1 数据结构
#mermaid-svg-V8xJjMXGm5kD78IP{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-V8xJjMXGm5kD78IP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-V8xJjMXGm5kD78IP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-V8xJjMXGm5kD78IP .error-icon{fill:#552222;}#mermaid-svg-V8xJjMXGm5kD78IP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-V8xJjMXGm5kD78IP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-V8xJjMXGm5kD78IP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-V8xJjMXGm5kD78IP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-V8xJjMXGm5kD78IP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-V8xJjMXGm5kD78IP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-V8xJjMXGm5kD78IP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-V8xJjMXGm5kD78IP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-V8xJjMXGm5kD78IP .marker.cross{stroke:#333333;}#mermaid-svg-V8xJjMXGm5kD78IP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-V8xJjMXGm5kD78IP p{margin:0;}#mermaid-svg-V8xJjMXGm5kD78IP .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-V8xJjMXGm5kD78IP .cluster-label text{fill:#333;}#mermaid-svg-V8xJjMXGm5kD78IP .cluster-label span{color:#333;}#mermaid-svg-V8xJjMXGm5kD78IP .cluster-label span p{background-color:transparent;}#mermaid-svg-V8xJjMXGm5kD78IP .label text,#mermaid-svg-V8xJjMXGm5kD78IP span{fill:#333;color:#333;}#mermaid-svg-V8xJjMXGm5kD78IP .node rect,#mermaid-svg-V8xJjMXGm5kD78IP .node circle,#mermaid-svg-V8xJjMXGm5kD78IP .node ellipse,#mermaid-svg-V8xJjMXGm5kD78IP .node polygon,#mermaid-svg-V8xJjMXGm5kD78IP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-V8xJjMXGm5kD78IP .rough-node .label text,#mermaid-svg-V8xJjMXGm5kD78IP .node .label text,#mermaid-svg-V8xJjMXGm5kD78IP .image-shape .label,#mermaid-svg-V8xJjMXGm5kD78IP .icon-shape .label{text-anchor:middle;}#mermaid-svg-V8xJjMXGm5kD78IP .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-V8xJjMXGm5kD78IP .rough-node .label,#mermaid-svg-V8xJjMXGm5kD78IP .node .label,#mermaid-svg-V8xJjMXGm5kD78IP .image-shape .label,#mermaid-svg-V8xJjMXGm5kD78IP .icon-shape .label{text-align:center;}#mermaid-svg-V8xJjMXGm5kD78IP .node.clickable{cursor:pointer;}#mermaid-svg-V8xJjMXGm5kD78IP .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-V8xJjMXGm5kD78IP .arrowheadPath{fill:#333333;}#mermaid-svg-V8xJjMXGm5kD78IP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-V8xJjMXGm5kD78IP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-V8xJjMXGm5kD78IP .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-V8xJjMXGm5kD78IP .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-V8xJjMXGm5kD78IP .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-V8xJjMXGm5kD78IP .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-V8xJjMXGm5kD78IP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-V8xJjMXGm5kD78IP .cluster text{fill:#333;}#mermaid-svg-V8xJjMXGm5kD78IP .cluster span{color:#333;}#mermaid-svg-V8xJjMXGm5kD78IP div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-V8xJjMXGm5kD78IP .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-V8xJjMXGm5kD78IP rect.text{fill:none;stroke-width:0;}#mermaid-svg-V8xJjMXGm5kD78IP .icon-shape,#mermaid-svg-V8xJjMXGm5kD78IP .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-V8xJjMXGm5kD78IP .icon-shape p,#mermaid-svg-V8xJjMXGm5kD78IP .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-V8xJjMXGm5kD78IP .icon-shape .label rect,#mermaid-svg-V8xJjMXGm5kD78IP .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-V8xJjMXGm5kD78IP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-V8xJjMXGm5kD78IP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-V8xJjMXGm5kD78IP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 日志框架
ASP.NET Core ILogger
Java SLF4J / Logback
ILogger
LogLevel
ILoggerProvider
Logger
LoggerFactory
Appender
Layout
15.2 代码对照
csharp
// C# ILogger
public class MyService(ILogger<MyService> logger) {
public void DoWork(string step) {
logger.LogInformation("Starting work");
try {
DoSomethingRisky();
} catch (Exception ex) {
// 结构化日志:第 1 个参数异常,第 2 个参数模板,第 3+ 个参数占位值
logger.LogError(ex, "Failed at {Step}", step);
throw;
}
}
}
java
// Java SLF4J + Logback
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
public class MyService {
private static final Logger logger = LoggerFactory.getLogger(MyService.class);
public void doWork(String step) {
logger.info("Starting work");
try {
doSomethingRisky();
} catch (Exception ex) {
// SLF4J: 最后一个参数若为 Throwable 则判定为异常 + 占位符填充
logger.error("Failed at {}", step, ex);
throw ex;
}
}
}
15.3 日志级别对照
| C# | Java SLF4J | 用途 |
|---|---|---|
Trace |
TRACE |
最详细 |
Debug |
DEBUG |
调试 |
Information |
INFO |
信息 |
Warning |
WARN |
警告 |
Error |
ERROR |
错误 |
Critical |
ERROR |
严重 |
十六、测试对照
16.1 数据结构
#mermaid-svg-XtPYSh604ahPY1VE{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-XtPYSh604ahPY1VE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XtPYSh604ahPY1VE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XtPYSh604ahPY1VE .error-icon{fill:#552222;}#mermaid-svg-XtPYSh604ahPY1VE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XtPYSh604ahPY1VE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XtPYSh604ahPY1VE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XtPYSh604ahPY1VE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XtPYSh604ahPY1VE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XtPYSh604ahPY1VE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XtPYSh604ahPY1VE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XtPYSh604ahPY1VE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XtPYSh604ahPY1VE .marker.cross{stroke:#333333;}#mermaid-svg-XtPYSh604ahPY1VE svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XtPYSh604ahPY1VE p{margin:0;}#mermaid-svg-XtPYSh604ahPY1VE .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-XtPYSh604ahPY1VE .cluster-label text{fill:#333;}#mermaid-svg-XtPYSh604ahPY1VE .cluster-label span{color:#333;}#mermaid-svg-XtPYSh604ahPY1VE .cluster-label span p{background-color:transparent;}#mermaid-svg-XtPYSh604ahPY1VE .label text,#mermaid-svg-XtPYSh604ahPY1VE span{fill:#333;color:#333;}#mermaid-svg-XtPYSh604ahPY1VE .node rect,#mermaid-svg-XtPYSh604ahPY1VE .node circle,#mermaid-svg-XtPYSh604ahPY1VE .node ellipse,#mermaid-svg-XtPYSh604ahPY1VE .node polygon,#mermaid-svg-XtPYSh604ahPY1VE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XtPYSh604ahPY1VE .rough-node .label text,#mermaid-svg-XtPYSh604ahPY1VE .node .label text,#mermaid-svg-XtPYSh604ahPY1VE .image-shape .label,#mermaid-svg-XtPYSh604ahPY1VE .icon-shape .label{text-anchor:middle;}#mermaid-svg-XtPYSh604ahPY1VE .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-XtPYSh604ahPY1VE .rough-node .label,#mermaid-svg-XtPYSh604ahPY1VE .node .label,#mermaid-svg-XtPYSh604ahPY1VE .image-shape .label,#mermaid-svg-XtPYSh604ahPY1VE .icon-shape .label{text-align:center;}#mermaid-svg-XtPYSh604ahPY1VE .node.clickable{cursor:pointer;}#mermaid-svg-XtPYSh604ahPY1VE .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-XtPYSh604ahPY1VE .arrowheadPath{fill:#333333;}#mermaid-svg-XtPYSh604ahPY1VE .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-XtPYSh604ahPY1VE .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-XtPYSh604ahPY1VE .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XtPYSh604ahPY1VE .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-XtPYSh604ahPY1VE .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XtPYSh604ahPY1VE .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-XtPYSh604ahPY1VE .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-XtPYSh604ahPY1VE .cluster text{fill:#333;}#mermaid-svg-XtPYSh604ahPY1VE .cluster span{color:#333;}#mermaid-svg-XtPYSh604ahPY1VE div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-XtPYSh604ahPY1VE .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-XtPYSh604ahPY1VE rect.text{fill:none;stroke-width:0;}#mermaid-svg-XtPYSh604ahPY1VE .icon-shape,#mermaid-svg-XtPYSh604ahPY1VE .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XtPYSh604ahPY1VE .icon-shape p,#mermaid-svg-XtPYSh604ahPY1VE .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-XtPYSh604ahPY1VE .icon-shape .label rect,#mermaid-svg-XtPYSh604ahPY1VE .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XtPYSh604ahPY1VE .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-XtPYSh604ahPY1VE .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-XtPYSh604ahPY1VE :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 测试框架
xUnit / NUnit
JUnit 5
Fact
Theory
Trait
@Test
@ParameterizedTest
@Tag
16.2 代码对照
csharp
// C# xUnit
public class Calculator {
public int Add(int a, int b) => a + b;
}
using Xunit;
public class CalculatorTest {
[Fact]
public void Add_TwoNumbers_ReturnsSum() {
var calc = new Calculator();
var result = calc.Add(2, 3);
Assert.Equal(5, result);
}
[Theory]
[InlineData(2, 3, 5)]
[InlineData(-1, 1, 0)]
public void Add_MultipleInputs_ReturnsExpected(int a, int b, int expected) {
var calc = new Calculator();
Assert.Equal(expected, calc.Add(a, b));
}
}
java
// Java JUnit 5
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
class Calculator {
int add(int a, int b) {
return a + b;
}
}
class CalculatorTest {
@Test
void add_TwoNumbers_ReturnsSum() {
Calculator calc = new Calculator();
assertEquals(5, calc.add(2, 3));
}
@ParameterizedTest
@CsvSource({"2, 3, 5", "-1, 1, 0"})
void add_MultipleInputs_ReturnsExpected(int a, int b, int expected) {
Calculator calc = new Calculator();
assertEquals(expected, calc.add(a, b));
}
}
十七、构建工具对照
17.1 数据结构
#mermaid-svg-1knwqr3wjo9HDlgw{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-1knwqr3wjo9HDlgw .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-1knwqr3wjo9HDlgw .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-1knwqr3wjo9HDlgw .error-icon{fill:#552222;}#mermaid-svg-1knwqr3wjo9HDlgw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1knwqr3wjo9HDlgw .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-1knwqr3wjo9HDlgw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1knwqr3wjo9HDlgw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1knwqr3wjo9HDlgw .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-1knwqr3wjo9HDlgw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1knwqr3wjo9HDlgw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1knwqr3wjo9HDlgw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1knwqr3wjo9HDlgw .marker.cross{stroke:#333333;}#mermaid-svg-1knwqr3wjo9HDlgw svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1knwqr3wjo9HDlgw p{margin:0;}#mermaid-svg-1knwqr3wjo9HDlgw .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-1knwqr3wjo9HDlgw .cluster-label text{fill:#333;}#mermaid-svg-1knwqr3wjo9HDlgw .cluster-label span{color:#333;}#mermaid-svg-1knwqr3wjo9HDlgw .cluster-label span p{background-color:transparent;}#mermaid-svg-1knwqr3wjo9HDlgw .label text,#mermaid-svg-1knwqr3wjo9HDlgw span{fill:#333;color:#333;}#mermaid-svg-1knwqr3wjo9HDlgw .node rect,#mermaid-svg-1knwqr3wjo9HDlgw .node circle,#mermaid-svg-1knwqr3wjo9HDlgw .node ellipse,#mermaid-svg-1knwqr3wjo9HDlgw .node polygon,#mermaid-svg-1knwqr3wjo9HDlgw .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-1knwqr3wjo9HDlgw .rough-node .label text,#mermaid-svg-1knwqr3wjo9HDlgw .node .label text,#mermaid-svg-1knwqr3wjo9HDlgw .image-shape .label,#mermaid-svg-1knwqr3wjo9HDlgw .icon-shape .label{text-anchor:middle;}#mermaid-svg-1knwqr3wjo9HDlgw .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-1knwqr3wjo9HDlgw .rough-node .label,#mermaid-svg-1knwqr3wjo9HDlgw .node .label,#mermaid-svg-1knwqr3wjo9HDlgw .image-shape .label,#mermaid-svg-1knwqr3wjo9HDlgw .icon-shape .label{text-align:center;}#mermaid-svg-1knwqr3wjo9HDlgw .node.clickable{cursor:pointer;}#mermaid-svg-1knwqr3wjo9HDlgw .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-1knwqr3wjo9HDlgw .arrowheadPath{fill:#333333;}#mermaid-svg-1knwqr3wjo9HDlgw .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-1knwqr3wjo9HDlgw .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-1knwqr3wjo9HDlgw .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1knwqr3wjo9HDlgw .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-1knwqr3wjo9HDlgw .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1knwqr3wjo9HDlgw .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-1knwqr3wjo9HDlgw .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-1knwqr3wjo9HDlgw .cluster text{fill:#333;}#mermaid-svg-1knwqr3wjo9HDlgw .cluster span{color:#333;}#mermaid-svg-1knwqr3wjo9HDlgw div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-1knwqr3wjo9HDlgw .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-1knwqr3wjo9HDlgw rect.text{fill:none;stroke-width:0;}#mermaid-svg-1knwqr3wjo9HDlgw .icon-shape,#mermaid-svg-1knwqr3wjo9HDlgw .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1knwqr3wjo9HDlgw .icon-shape p,#mermaid-svg-1knwqr3wjo9HDlgw .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-1knwqr3wjo9HDlgw .icon-shape .label rect,#mermaid-svg-1knwqr3wjo9HDlgw .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1knwqr3wjo9HDlgw .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-1knwqr3wjo9HDlgw .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-1knwqr3wjo9HDlgw :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 构建工具
dotnet CLI
Maven
dotnet build
dotnet test
dotnet publish
dotnet run
mvn compile
mvn test
mvn package
mvn spring-boot:run
17.2 命令对照
| 功能 | .NET | Maven |
|---|---|---|
| 编译 | dotnet build |
mvn compile |
| 清理 | dotnet clean |
mvn clean |
| 测试 | dotnet test |
mvn test |
| 打包 | dotnet pack |
mvn package |
| 运行 | dotnet run |
mvn spring-boot:run |
| 发布 | dotnet publish -c Release |
mvn package -DskipTests |
| 依赖还原 | dotnet restore |
mvn dependency:resolve |
| 测试覆盖率 | coverlet |
jacoco |
| 静态分析 | dotnet format |
checkstyle |