跨领域能力迁移

难度等级:⭐⭐⭐ 前置知识:.NET 基础、Java 基础 后续衔接:架构设计

学习路径


一、Web 框架迁移

1.1 ASP.NET Core → Spring Boot

ASP.NET Core 和 Spring Boot 分别是 .NET 和 Java 生态中最主流的 Web 框架。两者在设计理念上有诸多相似之处,但在具体实现和 API 设计上存在差异。

启动入口对比

对比维度 ASP.NET Core Spring Boot
入口类 Program.cs Application.java
启动方式 WebApplication.CreateBuilder(args) SpringApplication.run(Application.class, args)
配置加载 builder.Configuration @ConfigurationProperties
服务注册 builder.Services.AddXXX() @Bean / @Component

ASP.NET Core 示例:

var builder = WebApplication.CreateBuilder(args);

// 注册服务
builder.Services.AddControllers();
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("Default")));

var app = builder.Build();

// 配置中间件管道
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();

app.Run();

Spring Boot 示例:

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

@Configuration
public class AppConfig {
    @Bean
    public UserService userService() {
        return new UserServiceImpl();
    }
}

@RestController
@RequestMapping("/api/users")
public class UserController {
    private final UserService userService;
    
    public UserController(UserService userService) {
        this.userService = userService;
    }
}

路由定义对比

对比维度 ASP.NET Core Spring Boot
控制器注解 [ApiController] @RestController
路由前缀 [Route("api/[controller]")] @RequestMapping("/api/users")
GET 请求 [HttpGet] / [HttpGet("{id}")] @GetMapping / @GetMapping("/{id}")
POST 请求 [HttpPost] @PostMapping
PUT 请求 [HttpPut("{id}")] @PutMapping("/{id}")
DELETE 请求 [HttpDelete("{id}")] @DeleteMapping("/{id}")
路由参数 [FromRoute] int id @PathVariable Long id
查询参数 [FromQuery] string name @RequestParam String name
请求体 [FromBody] UserDto dto @RequestBody UserDto dto

ASP.NET Core 示例:

[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
    [HttpGet]
    public async Task<ActionResult<IEnumerable<UserDto>>> GetAll() { ... }

    [HttpGet("{id}")]
    public async Task<ActionResult<UserDto>> GetById(int id) { ... }

    [HttpPost]
    public async Task<ActionResult<UserDto>> Create([FromBody] CreateUserDto dto) { ... }

    [HttpPut("{id}")]
    public async Task<ActionResult> Update(int id, [FromBody] UpdateUserDto dto) { ... }

    [HttpDelete("{id}")]
    public async Task<ActionResult> Delete(int id) { ... }
}

Spring Boot 示例:

@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @GetMapping
    public ResponseEntity<List<UserDto>> getAll() { ... }

    @GetMapping("/{id}")
    public ResponseEntity<UserDto> getById(@PathVariable Long id) { ... }

    @PostMapping
    public ResponseEntity<UserDto> create(@RequestBody CreateUserDto dto) { ... }

    @PutMapping("/{id}")
    public ResponseEntity<Void> update(@PathVariable Long id, @RequestBody UpdateUserDto dto) { ... }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> delete(@PathVariable Long id) { ... }
}

中间件/过滤器对比

对比维度 ASP.NET Core Spring Boot
中间件注册 app.UseXXX() @Component 实现 Filter / WebFilter
自定义中间件 InvokeAsync(HttpContext context) doFilter(ServletRequest, ServletResponse, FilterChain)
执行顺序 按注册顺序 @Order 注解控制
短路 context.Response.WriteAsync() 后不调用 next() 不调用 chain.doFilter()

ASP.NET Core 中间件示例:

public class RequestLoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<RequestLoggingMiddleware> _logger;

    public RequestLoggingMiddleware(RequestDelegate next, ILogger<RequestLoggingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        _logger.LogInformation($"Request: {context.Request.Method} {context.Request.Path}");
        await _next(context);
        _logger.LogInformation($"Response: {context.Response.StatusCode}");
    }
}

// 注册方式
app.UseMiddleware<RequestLoggingMiddleware>();

Spring Boot 过滤器示例:

@Component
@Order(1)
public class RequestLoggingFilter implements Filter {
    
    private static final Logger logger = LoggerFactory.getLogger(RequestLoggingFilter.class);
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        logger.info("Request: {} {}", httpRequest.getMethod(), httpRequest.getRequestURI());
        
        chain.doFilter(request, response);
        
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        logger.info("Response: {}", httpResponse.getStatus());
    }
}

依赖注入对比

对比维度 ASP.NET Core Spring Boot
生命周期 Transient / Scoped / Singleton @Scope("prototype") / @RequestScope / @Singleton
注册方式 services.AddScoped<Interface, Impl>() @Component / @Service / @Repository / @Bean
构造函数注入 自动解析 自动解析(单构造函数无需注解)
属性注入 不支持(需第三方容器) @Autowired / @Resource
方法注入 不支持 @Autowired 方法

ASP.NET Core 示例:

// 服务注册
builder.Services.AddTransient<IEmailService, EmailService>();
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddSingleton<ICacheService, MemoryCacheService>();

// 构造函数注入(推荐)
public class UserController : ControllerBase
{
    private readonly IUserService _userService;
    
    public UserController(IUserService userService)
    {
        _userService = userService;
    }
}

Spring Boot 示例:

// 组件扫描自动注册
@Service
public class UserServiceImpl implements UserService {
    private final UserRepository userRepository;
    
    // 单构造函数自动注入,无需 @Autowired
    public UserServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

// 配置类中手动注册
@Configuration
public class AppConfig {
    @Bean
    @Scope("prototype")
    public EmailService emailService() {
        return new EmailServiceImpl();
    }
}

配置管理对比

对比维度 ASP.NET Core Spring Boot
配置文件 appsettings.json application.yml / application.properties
环境配置 appsettings.Development.json application-dev.yml
读取方式 Configuration["Key"] / IOptions<T> @Value("${key}") / @ConfigurationProperties
配置绑定 builder.Configuration.GetSection("Section").Get<T>() @ConfigurationProperties(prefix = "xxx")
热刷新 需手动实现 @RefreshScope + Spring Cloud Config

ASP.NET Core 配置示例:

{
  "AppSettings": {
    "SiteName": "MyApp",
    "MaxUploadSize": 10485760
  },
  "ConnectionStrings": {
    "Default": "Server=localhost;Database=mydb;"
  }
}
public class AppSettings
{
    public string SiteName { get; set; }
    public int MaxUploadSize { get; set; }
}

builder.Services.Configure<AppSettings>(builder.Configuration.GetSection("AppSettings"));

public class HomeController : ControllerBase
{
    private readonly AppSettings _settings;
    
    public HomeController(IOptions<AppSettings> options)
    {
        _settings = options.Value;
    }
}

Spring Boot 配置示例:

app:
  settings:
    site-name: MyApp
    max-upload-size: 10485760
@ConfigurationProperties(prefix = "app.settings")
@Component
public class AppSettings {
    private String siteName;
    private int maxUploadSize;
    // getters and setters
}

@RestController
public class HomeController {
    private final AppSettings settings;
    
    public HomeController(AppSettings settings) {
        this.settings = settings;
    }
}

异常处理对比

对比维度 ASP.NET Core Spring Boot
全局异常处理 UseExceptionHandler() / IExceptionFilter @ControllerAdvice + @ExceptionHandler
自定义异常 继承 Exception 继承 RuntimeException
状态码设置 StatusCode(404) @ResponseStatus(HttpStatus.NOT_FOUND)
验证异常 ModelState.IsValid @Valid + MethodArgumentNotValidException

ASP.NET Core 全局异常处理:

public class GlobalExceptionFilter : IExceptionFilter
{
    private readonly ILogger<GlobalExceptionFilter> _logger;

    public GlobalExceptionFilter(ILogger<GlobalExceptionFilter> logger)
    {
        _logger = logger;
    }

    public void OnException(ExceptionContext context)
    {
        _logger.LogError(context.Exception, "Unhandled exception");
        
        var response = new { code = 500, message = "Internal Server Error" };
        context.Result = new JsonResult(response)
        {
            StatusCode = StatusCodes.Status500InternalServerError
        };
    }
}

builder.Services.AddControllers(options =>
{
    options.Filters.Add<GlobalExceptionFilter>();
});

Spring Boot 全局异常处理:

@RestControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResponseEntity<ErrorResponse> handleException(Exception e) {
        ErrorResponse response = new ErrorResponse(500, "Internal Server Error", e.getMessage());
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
    }
    
    @ExceptionHandler(ResourceNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException e) {
        ErrorResponse response = new ErrorResponse(404, e.getMessage(), null);
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response);
    }
    
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseEntity<ErrorResponse> handleValidation(MethodArgumentNotValidException e) {
        String message = e.getBindingResult().getFieldErrors().stream()
            .map(FieldError::getDefaultMessage)
            .collect(Collectors.joining(", "));
        ErrorResponse response = new ErrorResponse(400, "Validation Failed", message);
        return ResponseEntity.badRequest().body(response);
    }
}

1.2 核心差异

差异点 .NET (ASP.NET Core) Java (Spring Boot) 迁移建议
DI 容器 内置轻量级 DI,功能相对简单 Spring IoC 是核心,功能强大 .NET 开发者需理解 Spring 的 Bean 生命周期
中间件管道 基于 RequestDelegate 链式调用 基于 Servlet Filter 链 理解执行顺序的差异
配置刷新 默认不自动刷新,需手动重载 支持 @RefreshScope 热刷新 分布式配置场景注意差异
异步模型 async/await 语法简洁 CompletableFuture / 虚拟线程 Java 21+ 虚拟线程可简化异步模型
启动速度 较快 相对较慢(可通过 GraalVM 改善) 冷启动场景考虑 GraalVM
内存占用 较低 相对较高 容器化部署时调整 JVM 参数

二、ORM 框架迁移

2.1 EF Core → Spring Data JPA

Entity Framework Core 和 Spring Data JPA 都是各自生态中最流行的 ORM 框架。

实体定义对比

对比维度 EF Core Spring Data JPA
实体标记 普通类 + DbSet<T> @Entity 注解
主键 [Key] / 约定 @Id + @GeneratedValue
列映射 [Column("name")] @Column(name = "name")
表映射 [Table("users")] @Table(name = "users")
必填字段 [Required] @Column(nullable = false)
长度限制 [MaxLength(100)] @Column(length = 100)
忽略字段 [NotMapped] @Transient
时间戳 [Timestamp] @Version(乐观锁)

EF Core 实体示例:

[Table("users")]
public class User
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    [Column("user_name")]
    [Required]
    [MaxLength(100)]
    public string UserName { get; set; }

    [Column("email")]
    [MaxLength(200)]
    public string Email { get; set; }

    [NotMapped]
    public string FullName => $"{UserName} ({Email})";

    [Timestamp]
    public byte[] RowVersion { get; set; }

    public ICollection<Order> Orders { get; set; }
}

public class AppDbContext : DbContext
{
    public DbSet<User> Users { get; set; }
    public DbSet<Order> Orders { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<User>()
            .HasIndex(u => u.Email)
            .IsUnique();
            
        modelBuilder.Entity<User>()
            .HasMany(u => u.Orders)
            .WithOne(o => o.User)
            .HasForeignKey(o => o.UserId);
    }
}

Spring Data JPA 实体示例:

@Entity
@Table(name = "users", indexes = {
    @Index(name = "idx_email", columnList = "email", unique = true)
})
public class User {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "user_name", nullable = false, length = 100)
    private String userName;

    @Column(name = "email", length = 200)
    private String email;

    @Transient
    public String getFullName() {
        return userName + " (" + email + ")";
    }

    @Version
    private Long version;

    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<Order> orders = new ArrayList<>();
}

@Entity
@Table(name = "orders")
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id", nullable = false)
    private User user;
}

Repository 定义对比

对比维度 EF Core Spring Data JPA
基础接口 DbSet<T> JpaRepository<T, ID>
查询方法 LINQ 方法名派生 / @Query
分页查询 .Skip().Take() Pageable 参数
排序 .OrderBy() Sort 参数
自定义查询 FromSqlRaw() @Query(nativeQuery = true)
异步操作 .ToListAsync() 返回 CompletableFuture 或直接返回

EF Core 查询示例:

public class UserService
{
    private readonly AppDbContext _context;

    public UserService(AppDbContext context) => _context = context;

    public async Task<User?> GetByIdAsync(int id) =>
        await _context.Users.FindAsync(id);

    public async Task<List<User>> GetByNameAsync(string name) =>
        await _context.Users
            .Where(u => u.UserName.Contains(name))
            .OrderBy(u => u.UserName)
            .ToListAsync();

    public async Task<(List<User> Users, int Total)> GetPagedAsync(int page, int size)
    {
        var query = _context.Users.AsQueryable();
        var total = await query.CountAsync();
        var users = await query
            .OrderBy(u => u.Id)
            .Skip((page - 1) * size)
            .Take(size)
            .ToListAsync();
        return (users, total);
    }
}

Spring Data JPA 查询示例:

public interface UserRepository extends JpaRepository<User, Long> {
    
    List<User> findByUserNameContaining(String name);
    
    Page<User> findByUserNameContaining(String name, Pageable pageable);
    
    @Query("SELECT u FROM User u WHERE u.email LIKE %:email%")
    List<User> findByEmailContaining(@Param("email") String email);
    
    @Query(value = "SELECT * FROM users WHERE is_active = 1", nativeQuery = true)
    List<User> findActiveUsers();
}

@Service
public class UserService {
    private final UserRepository userRepository;
    
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    
    public Optional<User> getById(Long id) {
        return userRepository.findById(id);
    }
    
    public Page<User> getPaged(int page, int size) {
        Pageable pageable = PageRequest.of(page - 1, size, Sort.by("id"));
        return userRepository.findAll(pageable);
    }
}

数据库迁移对比

对比维度 EF Core Migrations Flyway / Liquibase
迁移文件 C# 代码类 SQL 脚本 / XML
生成方式 dotnet ef migrations add 手动编写或工具生成
执行方式 dotnet ef database update 应用启动时自动执行
版本控制 基于时间戳的类名 基于文件名排序
回滚 dotnet ef migrations remove 需手动编写回滚脚本

EF Core 迁移示例:

# 添加迁移
dotnet ef migrations add AddUserTable

# 生成的迁移类
public partial class AddUserTable : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.CreateTable(
            name: "users",
            columns: table => new
            {
                Id = table.Column<int>(nullable: true)
                    .Annotation("SqlServer:Identity", "1, 1"),
                UserName = table.Column<string>(maxLength: 100, nullable: false),
                Email = table.Column<string>(maxLength: 200, nullable: true)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_users", x => x.Id);
            });
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropTable(name: "users");
    }
}

Flyway 迁移示例:

-- V1__Create_users_table.sql
CREATE TABLE users (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    user_name VARCHAR(100) NOT NULL,
    email VARCHAR(200),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE UNIQUE INDEX idx_email ON users(email);
# application.yml 配置
spring:
  flyway:
    enabled: true
    locations: classpath:db/migration
    baseline-on-migrate: true

2.2 Dapper → MyBatis

Dapper 和 MyBatis 都是轻量级 SQL 映射器,允许开发者直接编写 SQL 语句。

核心概念对比

对比维度 Dapper MyBatis
SQL 编写 内联 C# 字符串 XML 映射文件 / 注解
参数映射 匿名对象 / 动态参数 #{param} 语法
结果映射 自动映射 / SqlMapper resultMap 配置
动态 SQL 手动拼接 <if>, <choose>, <foreach>
多结果集 QueryMultiple <collection> 嵌套查询
缓存 无内置缓存 一级/二级缓存

Dapper 示例:

public class UserRepository
{
    private readonly IDbConnection _db;

    public UserRepository(IConfiguration config)
    {
        _db = new SqlConnection(config.GetConnectionString("Default"));
    }

    public async Task<User?> GetByIdAsync(int id)
    {
        var sql = "SELECT * FROM users WHERE id = @Id";
        return await _db.QueryFirstOrDefaultAsync<User>(sql, new { Id = id });
    }

    public async Task<List<User>> GetByNameAsync(string name)
    {
        var sql = "SELECT * FROM users WHERE user_name LIKE @Name ORDER BY created_at DESC";
        return (await _db.QueryAsync<User>(sql, new { Name = $"%{name}%" })).ToList();
    }

    public async Task<int> InsertAsync(User user)
    {
        var sql = @"INSERT INTO users (user_name, email) VALUES (@UserName, @Email);
                    SELECT CAST(SCOPE_IDENTITY() as int)";
        return await _db.ExecuteScalarAsync<int>(sql, user);
    }
}

MyBatis 示例:

public interface UserMapper {
    User findById(@Param("id") Long id);
    List<User> findByName(@Param("name") String name);
    int insert(User user);
}
<!-- UserMapper.xml -->
<mapper namespace="com.example.mapper.UserMapper">

    <resultMap id="userResultMap" type="User">
        <id property="id" column="id"/>
        <result property="userName" column="user_name"/>
        <result property="email" column="email"/>
    </resultMap>

    <select id="findById" resultMap="userResultMap">
        SELECT * FROM users WHERE id = #{id}
    </select>

    <select id="findByName" resultMap="userResultMap">
        SELECT * FROM users
        <where>
            <if test="name != null and name != ''">
                AND user_name LIKE CONCAT('%', #{name}, '%')
            </if>
        </where>
        ORDER BY created_at DESC
    </select>

    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO users (user_name, email)
        VALUES (#{userName}, #{email})
    </insert>

</mapper>

动态 SQL 对比

Dapper 动态查询(手动拼接):

public async Task<List<User>> SearchAsync(UserSearchCriteria criteria)
{
    var sql = new StringBuilder("SELECT * FROM users WHERE 1=1");
    var parameters = new DynamicParameters();

    if (!string.IsNullOrEmpty(criteria.Name))
    {
        sql.Append(" AND user_name LIKE @Name");
        parameters.Add("Name", $"%{criteria.Name}%");
    }

    if (criteria.Status.HasValue)
    {
        sql.Append(" AND status = @Status");
        parameters.Add("Status", criteria.Status.Value);
    }

    return (await _db.QueryAsync<User>(sql.ToString(), parameters)).ToList();
}

MyBatis 动态查询(XML 配置):

<select id="search" resultMap="userResultMap">
    SELECT * FROM users
    <where>
        <if test="name != null and name != ''">
            AND user_name LIKE CONCAT('%', #{name}, '%')
        </if>
        <if test="status != null">
            AND status = #{status}
        </if>
    </where>
    ORDER BY created_at DESC
</select>

<!-- foreach 处理 IN 查询 -->
<select id="findByIds" resultMap="userResultMap">
    SELECT * FROM users WHERE id IN
    <foreach item="id" collection="ids" open="(" separator="," close=")">
        #{id}
    </foreach>
</select>

三、微服务组件迁移

3.1 网关:Ocelot → Spring Cloud Gateway

API 网关是微服务架构中的关键组件,负责路由、限流、认证等功能。

核心功能对比

对比维度 Ocelot Spring Cloud Gateway
配置方式 ocelot.json application.yml / Java DSL
路由定义 ReRoutes 数组 spring.cloud.gateway.routes
负载均衡 内置(Consul/ETCD) 集成 LoadBalancer
限流 内置 RateLimiting RequestRateLimiter
认证 中间件方式 GatewayFilter
熔断 集成 Polly 集成 Resilience4j

Ocelot 配置示例:

{
  "ReRoutes": [
    {
      "DownstreamPathTemplate": "/api/users/{everything}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        { "Host": "user-service", "Port": 8080 }
      ],
      "UpstreamPathTemplate": "/users/{everything}",
      "UpstreamHttpMethod": ["GET", "POST", "PUT", "DELETE"],
      "RateLimitOptions": {
        "EnableRateLimiting": true,
        "Period": "1m",
        "Limit": 100
      }
    }
  ],
  "GlobalConfiguration": {
    "BaseUrl": "http://localhost:5000"
  }
}

Spring Cloud Gateway 配置示例:

spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/users/**
          filters:
            - StripPrefix=1
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 10
                redis-rate-limiter.burstCapacity: 100
            - name: CircuitBreaker
              args:
                name: userCircuitBreaker
                fallbackUri: forward:/fallback/user

      globalcors:
        corsConfigurations:
          '[/**]':
            allowedOrigins: "*"
            allowedMethods: "*"
// 自定义过滤器
@Component
public class AuthGatewayFilterFactory extends AbstractGatewayFilterFactory<AuthGatewayFilterFactory.Config> {
    
    public AuthGatewayFilterFactory() {
        super(Config.class);
    }
    
    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            String token = exchange.getRequest().getHeaders().getFirst("Authorization");
            if (token == null || !token.startsWith("Bearer ")) {
                exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                return exchange.getResponse().setComplete();
            }
            return chain.filter(exchange);
        };
    }
    
    public static class Config { }
}

3.2 服务调用:HttpClient + Polly → OpenFeign + Resilience4j

微服务间的通信通常使用 HTTP 客户端,配合容错库实现弹性调用。

核心功能对比

对比维度 HttpClient + Polly OpenFeign + Resilience4j
客户端定义 手动创建 HttpClient 声明式接口 + @FeignClient
重试策略 Policy.Handle<>().Retry() @Retry 注解
熔断策略 Policy.Handle<>().CircuitBreaker() @CircuitBreaker 注解
超时策略 Policy.Timeout() @Timeout 注解
负载均衡 手动集成 自动集成 LoadBalancer
降级处理 Policy.Wrap() fallback 属性

HttpClient + Polly 示例:

public class UserServiceClient
{
    private readonly HttpClient _httpClient;
    private readonly AsyncPolicy<HttpResponseMessage> _retryPolicy;
    private readonly AsyncPolicy<HttpResponseMessage> _circuitBreakerPolicy;

    public UserServiceClient(HttpClient httpClient)
    {
        _httpClient = httpClient;
        
        // 重试策略:最多重试 3 次,指数退避
        _retryPolicy = Policy
            .Handle<HttpRequestException>()
            .OrResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
            .WaitAndRetryAsync(3, retryAttempt => 
                TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
        
        // 熔断策略:5 次失败后熔断 30 秒
        _circuitBreakerPolicy = Policy
            .Handle<HttpRequestException>()
            .OrResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
            .CircuitBreakerAsync(5, TimeSpan.FromSeconds(30));
    }

    public async Task<UserDto?> GetUserAsync(int id)
    {
        var wrappedPolicy = Policy.WrapAsync(_retryPolicy, _circuitBreakerPolicy);
        
        var response = await wrappedPolicy.ExecuteAsync(async () =>
            await _httpClient.GetAsync($"/api/users/{id}"));
        
        if (response.IsSuccessStatusCode)
        {
            var json = await response.Content.ReadAsStringAsync();
            return JsonSerializer.Deserialize<UserDto>(json);
        }
        return null;
    }
}

// 注册方式(推荐用 IHttpClientFactory)
builder.Services.AddHttpClient<IUserServiceClient, UserServiceClient>()
    .AddPolicyHandler(GetRetryPolicy())
    .AddPolicyHandler(GetCircuitBreakerPolicy());

OpenFeign + Resilience4j 示例:

// 声明式客户端
@FeignClient(name = "user-service", fallback = UserServiceFallback.class)
public interface UserServiceClient {
    
    @GetMapping("/api/users/{id}")
    UserDto getUser(@PathVariable("id") Long id);
    
    @PostMapping("/api/users")
    UserDto createUser(@RequestBody CreateUserDto dto);
}

// 降级实现
@Component
public class UserServiceFallback implements UserServiceClient {
    
    @Override
    public UserDto getUser(Long id) {
        return new UserDto(id, "Fallback User", "fallback@example.com");
    }
    
    @Override
    public UserDto createUser(CreateUserDto dto) {
        throw new RuntimeException("Service unavailable");
    }
}

// 使用方式
@Service
public class OrderService {
    private final UserServiceClient userServiceClient;
    
    public OrderService(UserServiceClient userServiceClient) {
        this.userServiceClient = userServiceClient;
    }
    
    public OrderDto createOrder(Long userId) {
        // 直接调用,熔断/重试由 Resilience4j 处理
        UserDto user = userServiceClient.getUser(userId);
        // ... 业务逻辑
    }
}
# application.yml - Resilience4j 配置
resilience4j:
  circuitbreaker:
    instances:
      userService:
        slidingWindowSize: 10
        failureRateThreshold: 50
        waitDurationInOpenState: 30s
        permittedNumberOfCallsInHalfOpenState: 3
  retry:
    instances:
      userService:
        maxAttempts: 3
        waitDuration: 1s
        retryExceptions:
          - java.io.IOException
  timelimiter:
    instances:
      userService:
        timeoutDuration: 5s

3.3 事件总线:CAP → Spring Cloud Stream

事件总线用于实现微服务间的异步通信和解耦。

核心功能对比

对比维度 CAP Spring Cloud Stream
消息中间件 RabbitMQ / Kafka / Azure Service Bus RabbitMQ / Kafka / RocketMQ
发布方式 ICapPublisher.Publish() StreamBridge.send() / Output Binding
订阅方式 [CapSubscribe("topic")] @StreamListener / @Bean Function
事务消息 内置(本地消息表) 需手动实现或使用 RocketMQ
重试机制 内置重试 + 死信队列 内置重试 + 死信主题
消息追踪 Dashboard 面板 Spring Cloud Sleuth / Micrometer

CAP 示例:

// 发布事件
public class OrderService
{
    private readonly ICapPublisher _capPublisher;

    public OrderService(ICapPublisher capPublisher)
    {
        _capPublisher = capPublisher;
    }

    public async Task CreateOrderAsync(CreateOrderDto dto)
    {
        // 业务逻辑 + 发布事件(在同一事务中)
        using var transaction = _dbContext.Database.BeginTransaction(_capPublisher, autoCommit: false);
        
        var order = new Order { ... };
        _dbContext.Orders.Add(order);
        await _dbContext.SaveChangesAsync();
        
        await _capPublisher.PublishAsync("order.created", new OrderCreatedEvent
        {
            OrderId = order.Id,
            UserId = order.UserId,
            Amount = order.Amount
        });
        
        transaction.Commit();
    }
}

// 订阅事件
public class NotificationConsumer : ICapSubscribe
{
    [CapSubscribe("order.created")]
    public async Task HandleOrderCreated(OrderCreatedEvent message)
    {
        // 发送通知
        await _notificationService.SendAsync(message.UserId, $"Order {message.OrderId} created");
    }
}

Spring Cloud Stream 示例:

// 配置
spring:
  cloud:
    stream:
      bindings:
        orderCreated-out-0:
          destination: order.created
        orderCreated-in-0:
          destination: order.created
          group: notification-service
      rabbit:
        bindings:
          orderCreated-in-0:
            consumer:
              maxAttempts: 3
              backOffInitialInterval: 1000

// 发布事件
@Service
public class OrderService {
    private final StreamBridge streamBridge;
    
    public OrderService(StreamBridge streamBridge) {
        this.streamBridge = streamBridge;
    }
    
    @Transactional
    public Order createOrder(CreateOrderDto dto) {
        Order order = new Order();
        orderRepository.save(order);
        
        // 发布事件
        streamBridge.send("orderCreated-out-0", new OrderCreatedEvent(
            order.getId(), order.getUserId(), order.getAmount()
        ));
        
        return order;
    }
}

// 订阅事件(函数式编程模型)
@Configuration
public class NotificationConsumer {
    
    @Bean
    public Consumer<OrderCreatedEvent> orderCreatedIn0() {
        return event -> {
            // 发送通知
            notificationService.send(event.getUserId(), 
                "Order " + event.getOrderId() + " created");
        };
    }
}

四、容错与弹性设计

4.1 Polly → Resilience4j

Polly 和 Resilience4j 分别是 .NET 和 Java 生态中最流行的容错库,提供了重试、熔断、超时、舱壁等弹性策略。

策略对比总览

策略类型 Polly Resilience4j 说明
重试 RetryPolicy @Retry / RetryRegistry 失败后自动重试
熔断 CircuitBreakerPolicy @CircuitBreaker / CircuitBreakerRegistry 防止级联故障
超时 TimeoutPolicy @TimeLimiter / TimeLimiterRegistry 限制操作执行时间
舱壁 BulkheadPolicy @Bulkhead / BulkheadRegistry 限制并发请求数
缓存 CachePolicy Cache(需集成 Caffeine) 缓存执行结果
降级 FallbackPolicy @Fallback / 手动实现 失败时提供备选方案
组合策略 Policy.Wrap() 注解叠加 / 自定义组合 多个策略组合使用

重试策略

Polly 重试:

// 简单重试
var retryPolicy = Policy
    .Handle<SqlException>(ex => ex.Number == 1205)
    .Retry(3);

await retryPolicy.ExecuteAsync(async () =>
{
    await _dbContext.SaveChangesAsync();
});

// 带等待时间的重试(指数退避)
var retryPolicy = Policy
    .Handle<HttpRequestException>()
    .WaitAndRetryAsync(
        retryCount: 3,
        sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
        onRetry: (exception, timeSpan, retryCount, context) =>
        {
            logger.LogWarning($"Retry {retryCount} after {timeSpan}: {exception.Message}");
        });

// 结合上下文的重试
var context = new Context { ["operation"] = "SaveOrder" };
await retryPolicy.ExecuteAsync(async (ctx) => {
    logger.LogInformation($"Executing {ctx["operation"]}");
    await SaveAsync();
}, context);

Resilience4j 重试:

// 注解方式
@Retry(name = "databaseOperation", fallbackMethod = "saveFallback")
public Order saveOrder(Order order) {
    return orderRepository.save(order);
}

public Order saveFallback(Order order, Throwable t) {
    logger.warn("Retry failed, using fallback: {}", t.getMessage());
    return null;
}

// 编程方式
RetryConfig config = RetryConfig.custom()
    .maxAttempts(3)
    .waitDuration(Duration.ofSeconds(1))
    .retryExceptions(SQLException.class, IOException.class)
    .ignoreExceptions(IllegalArgumentException.class)
    .build();

Retry retry = Retry.of("databaseOperation", config);

Order result = Retry.decorateSupplier(retry, () -> orderRepository.save(order));

// 事件监听
retry.getEventPublisher()
    .onRetry(event -> logger.warn("Retry attempt: {}", event))
    .onSuccess(event -> logger.info("Success after retries"));
# application.yml
resilience4j:
  retry:
    instances:
      databaseOperation:
        maxAttempts: 3
        waitDuration: 1s
        retryExceptions:
          - java.sql.SQLException
          - java.io.IOException
        ignoreExceptions:
          - java.lang.IllegalArgumentException

熔断策略

Polly 熔断:

// 基础熔断
var circuitBreaker = Policy
    .Handle<Exception>()
    .CircuitBreaker(
        exceptionsAllowedBeforeBreaking: 5,    // 连续 5 次失败后熔断
        durationOfBreak: TimeSpan.FromSeconds(30), // 熔断 30 秒
        onBreak: (ex, duration) => 
            logger.LogError($"Circuit broken: {ex.Message}"),
        onReset: () => 
            logger.LogInformation("Circuit reset"),
        onHalfOpen: () => 
            logger.LogInformation("Circuit half-open")
    );

// 高级熔断(基于失败率)
var advancedCircuitBreaker = Policy
    .Handle<Exception>()
    .AdvancedCircuitBreaker(
        failureThreshold: 0.5,           // 失败率超过 50%
        samplingDuration: TimeSpan.FromSeconds(30),  // 统计窗口 30 秒
        minimumThroughput: 10,           // 最少 10 次请求
        durationOfBreak: TimeSpan.FromSeconds(60)
    );

// 使用
try
{
    await circuitBreaker.ExecuteAsync(async () => await CallExternalService());
}
catch (BrokenCircuitException)
{
    // 熔断时的降级逻辑
    await UseCachedDataAsync();
}

Resilience4j 熔断:

// 注解方式
@CircuitBreaker(name = "externalService", fallbackMethod = "fallback")
@TimeLimiter(name = "externalService")
public CompletableFuture<String> callExternalService() {
    return CompletableFuture.supplyAsync(() -> {
        return restTemplate.getForObject(url, String.class);
    });
}

public CompletableFuture<String> fallback(Throwable t) {
    return CompletableFuture.completedFuture("Fallback data");
}

// 编程方式
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
    .failureRateThreshold(50)           // 失败率超过 50%
    .slowCallRateThreshold(80)          // 慢调用率超过 80%
    .slowCallDurationThreshold(Duration.ofSeconds(2))
    .permittedNumberOfCallsInHalfOpenState(3)
    .slidingWindowSize(10)
    .minimumNumberOfCalls(10)
    .slidingWindowType(SlidingWindowType.TIME_BASED)
    .waitDurationInOpenState(Duration.ofSeconds(30))
    .build();

CircuitBreaker circuitBreaker = CircuitBreaker.of("externalService", config);

// 状态监控
CircuitBreaker.State state = circuitBreaker.getState();
logger.info("Circuit breaker state: {}", state);

// 事件监听
circuitBreaker.getEventPublisher()
    .onStateTransition(event -> 
        logger.info("State changed from {} to {}", event.getStateTransition()))
    .onFailureRateExceeded(event -> 
        logger.error("Failure rate exceeded: {}", event.getFailureRate()));
# application.yml
resilience4j:
  circuitbreaker:
    instances:
      externalService:
        slidingWindowSize: 10
        minimumNumberOfCalls: 10
        failureRateThreshold: 50
        slowCallRateThreshold: 80
        slowCallDurationThreshold: 2s
        waitDurationInOpenState: 30s
        permittedNumberOfCallsInHalfOpenState: 3
  timelimiter:
    instances:
      externalService:
        timeoutDuration: 5s
        cancelRunningFuture: true

超时策略

Polly 超时:

// 固定超时
var timeoutPolicy = Policy.TimeoutAsync(TimeSpan.FromSeconds(10));

// 乐观超时(取消正在执行的操作)
var optimisticTimeout = Policy.TimeoutAsync(
    TimeSpan.FromSeconds(5),
    TimeoutStrategy.Optimistic);

// 组合使用
var policyWrap = Policy.WrapAsync(retryPolicy, circuitBreaker, timeoutPolicy);

await policyWrap.ExecuteAsync(async () =>
{
    await _httpClient.GetAsync(url);
});

Resilience4j 超时(TimeLimiter):

@TimeLimiter(name = "externalService")
public CompletableFuture<String> callWithTimeout() {
    return CompletableFuture.supplyAsync(() -> {
        return restTemplate.getForObject(url, String.class);
    });
}

// 编程方式
TimeLimiterConfig config = TimeLimiterConfig.custom()
    .timeoutDuration(Duration.ofSeconds(5))
    .cancelRunningFuture(true)
    .build();

TimeLimiter timeLimiter = TimeLimiter.of("externalService", config);

Supplier<CompletableFuture<String>> futureSupplier = () -> 
    CompletableFuture.supplyAsync(() -> callExternalService());

CompletableFuture<String> future = TimeLimiter.decorateFutureSupplier(
    timeLimiter, futureSupplier);

舱壁策略

Polly 舱壁:

// 固定大小的舱壁(限制并发数)
var bulkhead = Policy.BulkheadAsync(
    maxParallelization: 10,     // 最多 10 个并发
    maxQueuingActions: 5,       // 队列中最多 5 个等待
    onBulkheadRejected: (context) =>
        logger.LogWarning("Bulkhead full, rejecting request"));

// 使用
try
{
    await bulkhead.ExecuteAsync(async () => await ProcessRequest());
}
catch (BulkheadRejectedException)
{
    // 舱壁满时的降级处理
    return "Service temporarily unavailable";
}

Resilience4j 舱壁:

// 注解方式
@Bulkhead(name = "importService", type = Bulkhead.Type.THREADPOOL)
public CompletableFuture<String> importData() {
    return CompletableFuture.supplyAsync(() -> dataService.import());
}

// 编程方式 - 信号量舱壁
BulkheadConfig semaphoreConfig = BulkheadConfig.custom()
    .maxConcurrentCalls(10)
    .maxWaitDuration(Duration.ofMillis(500))
    .build();

Bulkhead semaphoreBulkhead = Bulkhead.of("importService", semaphoreConfig);

// 编程方式 - 线程池舱壁
ThreadPoolBulkheadConfig threadPoolConfig = ThreadPoolBulkheadConfig.custom()
    .maxThreadPoolSize(10)
    .coreThreadPoolSize(5)
    .queueCapacity(20)
    .build();

ThreadPoolBulkhead threadPoolBulkhead = ThreadPoolBulkhead.of("importService", threadPoolConfig);

// 使用
Supplier<String> supplier = Bulkhead.decorateSupplier(semaphoreBulkhead, 
    () -> dataService.import());
String result = supplier.get();
# application.yml
resilience4j:
  bulkhead:
    instances:
      importService:
        maxConcurrentCalls: 10
        maxWaitDuration: 500ms
  thread-pool-bulkhead:
    instances:
      importService:
        coreThreadPoolSize: 5
        maxThreadPoolSize: 10
        queueCapacity: 20

组合策略最佳实践

Polly 策略组合:

public static IAsyncPolicy<HttpResponseMessage> GetResiliencePolicy()
{
    // 1. 重试策略
    var retry = Policy<HttpResponseMessage>
        .Handle<HttpRequestException>()
        .OrResult(r => !r.IsSuccessStatusCode)
        .WaitAndRetryAsync(3, i => TimeSpan.FromSeconds(Math.Pow(2, i)));

    // 2. 熔断策略
    var circuitBreaker = Policy<HttpResponseMessage>
        .Handle<HttpRequestException>()
        .OrResult(r => !r.IsSuccessStatusCode)
        .CircuitBreakerAsync(5, TimeSpan.FromSeconds(30));

    // 3. 超时策略
    var timeout = Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(10));

    // 4. 降级策略
    var fallback = Policy<HttpResponseMessage>
        .Handle<Exception>()
        .FallbackAsync(new HttpResponseMessage(HttpStatusCode.ServiceUnavailable)
        {
            Content = new StringContent("{\"message\":\"Service unavailable\"}")
        });

    // 组合顺序:Fallback -> Retry -> CircuitBreaker -> Timeout
    return Policy.WrapAsync(fallback, retry, circuitBreaker, timeout);
}

Resilience4j 策略组合:

// 注解叠加(推荐方式)
@CircuitBreaker(name = "userService", fallbackMethod = "fallback")
@Retry(name = "userService")
@TimeLimiter(name = "userService")
@Bulkhead(name = "userService")
public CompletableFuture<UserDto> getUser(Long id) {
    return CompletableFuture.supplyAsync(() -> 
        restTemplate.getForObject(url + "/" + id, UserDto.class));
}

public CompletableFuture<UserDto> fallback(Long id, Throwable t) {
    return CompletableFuture.completedFuture(new UserDto(id, "Unknown", "unknown"));
}

五、桌面到前端的思维迁移

5.1 WPF MVVM → Vue/React

从 WPF 桌面开发迁移到 Web 前端开发,核心的 MVVM 模式思想可以复用,但具体实现方式有较大差异。

核心概念对比

对比维度 WPF MVVM Vue 3 React
视图 XAML <template> JSX / TSX
ViewModel C# 类实现 INotifyPropertyChanged setup() / <script setup> 函数组件 + Hooks
Model POCO / Entity TypeScript Interface TypeScript Interface
数据绑定 {Binding Property} `` / v-model {property} / useState
命令 ICommand / RelayCommand @click / 方法 onClick / 事件处理函数
通知 PropertyChanged 事件 响应式系统(ref/reactive) setState / useState
路由 NavigationService Vue Router React Router
依赖注入 MEF / 第三方容器 provide/inject Context API

数据绑定对比

WPF 数据绑定:

// ViewModel
public class UserViewModel : INotifyPropertyChanged
{
    private string _userName;
    public string UserName
    {
        get => _userName;
        set
        {
            _userName = value;
            OnPropertyChanged();
        }
    }

    private bool _isLoading;
    public bool IsLoading
    {
        get => _isLoading;
        set
        {
            _isLoading = value;
            OnPropertyChanged();
        }
    }

    public ICommand LoadCommand { get; }
    public ICommand SaveCommand { get; }

    public UserViewModel()
    {
        LoadCommand = new RelayCommand(Load, CanLoad);
        SaveCommand = new RelayCommand(Save, CanSave);
    }

    private async void Load()
    {
        IsLoading = true;
        try
        {
            var user = await _userService.GetByIdAsync(1);
            UserName = user.Name;
        }
        finally
        {
            IsLoading = false;
        }
    }

    private bool CanLoad() => !IsLoading;
    private bool CanSave() => !string.IsNullOrEmpty(UserName);

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
<!-- XAML 视图 -->
<UserControl>
    <StackPanel>
        <TextBlock Text="{Binding UserName}" />
        <TextBox Text="{Binding UserName, UpdateSourceTrigger=PropertyChanged}" />
        <Button Content="Load" Command="{Binding LoadCommand}" />
        <Button Content="Save" Command="{Binding SaveCommand}" />
        <ProgressBar IsIndeterminate="{Binding IsLoading}" 
                     Visibility="{Binding IsLoading, Converter={StaticResource BoolToVisibility}}" />
    </StackPanel>
</UserControl>

Vue 3 数据绑定:

<template>
  <div>
    <p></p>
    <input v-model="userName" />
    <button @click="load" :disabled="isLoading">Load</button>
    <button @click="save" :disabled="!userName">Save</button>
    <div v-if="isLoading" class="loading">Loading...</div>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'

const userName = ref('')
const isLoading = ref(false)

const canSave = computed(() => userName.value !== '')

async function load() {
  if (isLoading.value) return
  isLoading.value = true
  try {
    const user = await userService.getById(1)
    userName.value = user.name
  } finally {
    isLoading.value = false
  }
}

async function save() {
  if (!canSave.value) return
  await userService.update({ name: userName.value })
}
</script>

React 数据绑定:

import { useState, useCallback } from 'react'

function UserView() {
  const [userName, setUserName] = useState('')
  const [isLoading, setIsLoading] = useState(false)

  const canSave = userName !== ''

  const load = useCallback(async () => {
    if (isLoading) return
    setIsLoading(true)
    try {
      const user = await userService.getById(1)
      setUserName(user.name)
    } finally {
      setIsLoading(false)
    }
  }, [isLoading])

  const save = useCallback(async () => {
    if (!canSave) return
    await userService.update({ name: userName })
  }, [userName, canSave])

  return (
    <div>
      <p>{userName}</p>
      <input value={userName} onChange={e => setUserName(e.target.value)} />
      <button onClick={load} disabled={isLoading}>Load</button>
      <button onClick={save} disabled={!canSave}>Save</button>
      {isLoading && <div className="loading">Loading...</div>}
    </div>
  )
}

命令与事件对比

对比维度 WPF Vue React
点击事件 Command="{Binding LoadCommand}" @click="load" onClick={load}
条件启用 CanExecute :disabled="isLoading" disabled={isLoading}
参数传递 CommandParameter="{Binding Id}" @click="handle(id)" onClick={() => handle(id)}
键盘事件 KeyDown="OnKeyDown" @keydown="handleKey" onKeyDown={handleKey}
自定义事件 event Action<Data> DataChanged emit('data-changed', data) onDataChanged(data) (props)

5.2 核心差异

差异点 WPF 桌面 Web 前端 迁移建议
渲染模式 同步渲染,UI 线程阻塞会卡死 异步渲染,虚拟 DOM 批量更新 理解浏览器渲染机制
状态管理 ViewModel 内置,INotifyPropertyChanged 需要额外库(Pinia/Redux/Zustand) 从小项目开始,逐步引入状态管理库
组件通信 事件聚合器 / 消息总线 Props down, Events up / Context / Provide-Inject 理解单向数据流思想
样式系统 XAML 样式 / 资源字典 CSS / SCSS / CSS-in-JS / Tailwind 学习 CSS 布局(Flexbox/Grid)
生命周期 Loaded / Unloaded onMounted / onUnmounted / useEffect 理解组件生命周期钩子
导航 NavigationService / 区域导航 客户端路由(Vue Router / React Router) 理解 SPA 路由原理
调试工具 Visual Studio 调试器 浏览器 DevTools / Vue DevTools / React DevTools 熟悉浏览器调试技巧

六、通用能力抽象

6.1 设计模式通用性

设计模式是跨语言的通用解决方案,以下是常见模式在不同语言中的实现对比。

创建型模式

模式 .NET 实现 Java 实现 核心思想
工厂方法 interface IFactory { T Create(); } interface Factory<T> { T create(); } 封装对象创建逻辑
抽象工厂 接口组合 + 泛型 接口组合 + 泛型 创建相关对象族
单例 static readonly + 静态属性 enum / volatile + 双重检查 全局唯一实例
建造者 Fluent API 链式调用 Builder 内部类 分步构建复杂对象
原型 ICloneable / 序列化克隆 Cloneable / 序列化 通过克隆创建对象

单例模式对比:

// .NET 线程安全单例
public sealed class Singleton
{
    private static readonly Lazy<Singleton> _instance = 
        new Lazy<Singleton>(() => new Singleton());
    
    public static Singleton Instance => _instance.Value;
    
    private Singleton() { }
}
// Java 线程安全单例(枚举方式)
public enum Singleton {
    INSTANCE;
    
    public void doSomething() { }
}

// 双重检查锁定
public class Singleton {
    private static volatile Singleton instance;
    
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

结构型模式

模式 .NET 实现 Java 实现 核心思想
适配器 类适配器 / 对象适配器 类适配器 / 对象适配器 接口转换
装饰器 包装类 + 接口组合 包装类 + 接口组合 动态添加职责
代理 RealProxy / 动态代理库 Proxy.newProxyInstance() 控制对象访问
外观 简化接口的高层类 简化接口的高层类 提供统一接口
组合 树形结构 + 统一接口 树形结构 + 统一接口 部分-整体层次结构

行为型模式

模式 .NET 实现 Java 实现 核心思想
观察者 event / IObserver<T> Observer / EventListener 一对多依赖关系
策略 接口 + 运行时切换 接口 + 运行时切换 算法可替换
模板方法 抽象类 + 虚方法 抽象类 + 抽象方法 定义算法骨架
责任链 链式处理器 链式过滤器 请求沿链传递
命令 ICommand 接口 Command 接口 请求封装为对象
状态 状态接口 + 上下文类 状态接口 + 上下文类 状态驱动行为

策略模式对比:

// .NET 策略模式
public interface IDiscountStrategy
{
    decimal CalculateDiscount(decimal total);
}

public class PercentageDiscount : IDiscountStrategy
{
    private readonly decimal _percentage;
    public PercentageDiscount(decimal percentage) => _percentage = percentage;
    public decimal CalculateDiscount(decimal total) => total * _percentage / 100;
}

public class FixedDiscount : IDiscountStrategy
{
    private readonly decimal _amount;
    public FixedDiscount(decimal amount) => _amount = amount;
    public decimal CalculateDiscount(decimal total) => Math.Min(_amount, total);
}

public class OrderService
{
    private IDiscountStrategy _strategy;
    
    public void SetDiscountStrategy(IDiscountStrategy strategy) => _strategy = strategy;
    
    public decimal CalculateTotal(decimal total) => _strategy.CalculateDiscount(total);
}
// Java 策略模式
public interface DiscountStrategy {
    BigDecimal calculateDiscount(BigDecimal total);
}

@Component
public class PercentageDiscount implements DiscountStrategy {
    private final BigDecimal percentage;
    
    public PercentageDiscount(BigDecimal percentage) {
        this.percentage = percentage;
    }
    
    @Override
    public BigDecimal calculateDiscount(BigDecimal total) {
        return total.multiply(percentage).divide(new BigDecimal("100"));
    }
}

@Service
public class OrderService {
    private DiscountStrategy strategy;
    
    public void setDiscountStrategy(DiscountStrategy strategy) {
        this.strategy = strategy;
    }
    
    public BigDecimal calculateTotal(BigDecimal total) {
        return strategy.calculateDiscount(total);
    }
}

6.2 架构模式通用性

架构模式关注系统的整体组织方式,以下是常见架构模式的跨语言实现。

分层架构

层级 职责 .NET 典型命名 Java 典型命名
表示层 用户界面 / API 端点 Controllers/ controller/
应用层 用例编排 Services/ service/
领域层 业务逻辑 / 领域模型 Domain/ domain/
基础设施层 数据访问 / 外部服务 Infrastructure/ infrastructure/

整洁架构 / 六边形架构

概念 说明 .NET 实现 Java 实现
端口 输入/输出接口 interface IUserService interface UserService
适配器 端口的具体实现 UserController / UserRepository UserController / UserRepository
领域核心 业务规则 Domain/Entities/ domain/entity/
依赖方向 外层依赖内层 内层不依赖外层 内层不依赖外层

CQRS 模式

组件 职责 .NET 实现 Java 实现
命令 修改状态 class CreateUserCommand class CreateUserCommand
查询 读取状态 class GetUserQuery class GetUserQuery
命令处理器 执行命令 IRequestHandler<Command> CommandHandler
查询处理器 执行查询 IRequestHandler<Query, Result> QueryHandler
事件 状态变更记录 class UserCreatedEvent class UserCreatedEvent

MediatR (CQRS) 对比 Axon Framework:

// .NET MediatR 命令
public class CreateUserCommand : IRequest<int>
{
    public string Name { get; set; }
    public string Email { get; set; }
}

public class CreateUserHandler : IRequestHandler<CreateUserCommand, int>
{
    private readonly IUserRepository _repository;
    
    public CreateUserHandler(IUserRepository repository) => _repository = repository;
    
    public async Task<int> Handle(CreateUserCommand request, CancellationToken ct)
    {
        var user = new User(request.Name, request.Email);
        await _repository.AddAsync(user);
        return user.Id;
    }
}

// 控制器中使用
[HttpPost]
public async Task<IActionResult> Create(CreateUserCommand command)
{
    var id = await _mediator.Send(command);
    return Ok(id);
}
// Java Axon 命令
@Aggregate
public class UserAggregate {
    @AggregateIdentifier
    private String id;
    private String name;
    private String email;
    
    @CommandHandler
    public UserAggregate(CreateUserCommand command) {
        AggregateLifecycle.apply(new UserCreatedEvent(
            command.getId(), command.getName(), command.getEmail()));
    }
    
    @EventSourcingHandler
    public void on(UserCreatedEvent event) {
        this.id = event.getId();
        this.name = event.getName();
        this.email = event.getEmail();
    }
}

// 控制器中使用
@RestController
public class UserController {
    private final CommandGateway commandGateway;
    
    public UserController(CommandGateway commandGateway) {
        this.commandGateway = commandGateway;
    }
    
    @PostMapping("/users")
    public CompletableFuture<String> create(@RequestBody CreateUserCommand command) {
        return commandGateway.send(command);
    }
}

6.3 分布式系统通用性

分布式系统的核心概念是跨语言和跨框架通用的。

分布式缓存

对比维度 .NET Java 通用概念
内存缓存 IMemoryCache ConcurrentHashMap 本地缓存
分布式缓存 IDistributedCache (Redis) RedisTemplate 远程缓存
缓存策略 MemoryCacheEntryOptions RedisCacheConfiguration TTL / 驱逐策略
缓存穿透 布隆过滤器 / 空值缓存 布隆过滤器 / 空值缓存 相同解决方案
缓存雪崩 随机过期时间 随机过期时间 相同解决方案
缓存击穿 分布式锁 分布式锁 相同解决方案

Redis 缓存对比:

// .NET Redis 缓存
public class CachedUserService : IUserService
{
    private readonly IDistributedCache _cache;
    private readonly IUserRepository _repository;
    
    public CachedUserService(IDistributedCache cache, IUserRepository repository)
    {
        _cache = cache;
        _repository = repository;
    }
    
    public async Task<User?> GetByIdAsync(int id)
    {
        var key = $"user:{id}";
        var cached = await _cache.GetStringAsync(key);
        
        if (cached != null)
            return JsonSerializer.Deserialize<User>(cached);
        
        var user = await _repository.GetByIdAsync(id);
        if (user != null)
        {
            var options = new DistributedCacheEntryOptions()
                .SetAbsoluteExpiration(TimeSpan.FromMinutes(30))
                .SetSlidingExpiration(TimeSpan.FromMinutes(10));
            
            await _cache.SetStringAsync(key, JsonSerializer.Serialize(user), options);
        }
        
        return user;
    }
}
// Java Redis 缓存(Spring Cache)
@Service
public class CachedUserService implements UserService {
    private final UserRepository repository;
    
    public CachedUserService(UserRepository repository) {
        this.repository = repository;
    }
    
    @Cacheable(value = "users", key = "#id", unless = "#result == null")
    public Optional<User> getById(Long id) {
        return repository.findById(id);
    }
    
    @CachePut(value = "users", key = "#user.id")
    public User update(User user) {
        return repository.save(user);
    }
    
    @CacheEvict(value = "users", key = "#id")
    public void delete(Long id) {
        repository.deleteById(id);
    }
}

分布式锁

对比维度 .NET Java 通用概念
Redis 锁 RedLock.net Redisson Redlock 算法
ZooKeeper 锁 ZooKeeperNet Curator 临时节点 + Watch
数据库锁 SELECT ... FOR UPDATE SELECT ... FOR UPDATE 悲观锁
乐观锁 版本号 / 时间戳 @Version / 版本号 CAS 思想

Redis 分布式锁对比:

// .NET RedLock
public class OrderService
{
    private readonly IDistributedLock _lock;
    
    public async Task ProcessOrderAsync(int orderId)
    {
        var resource = $"order:{orderId}";
        
        using (var lockResult = await _lock.AcquireAsync(resource, TimeSpan.FromSeconds(30)))
        {
            if (lockResult.IsAcquired)
            {
                // 执行业务逻辑
                await ProcessAsync(orderId);
            }
            else
            {
                throw new InvalidOperationException("Could not acquire lock");
            }
        }
    }
}
// Java Redisson 分布式锁
@Service
public class OrderService {
    private final RedissonClient redissonClient;
    
    public void processOrder(Long orderId) {
        String resource = "order:" + orderId;
        RLock lock = redissonClient.getLock(resource);
        
        try {
            if (lock.tryLock(30, 10, TimeUnit.SECONDS)) {
                try {
                    // 执行业务逻辑
                    process(orderId);
                } finally {
                    lock.unlock();
                }
            } else {
                throw new IllegalStateException("Could not acquire lock");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

分布式事务

模式 说明 .NET 实现 Java 实现
2PC 两阶段提交 TransactionScope JTA / Atomikos
TCC Try-Confirm-Cancel 手动实现 Seata TCC
Saga 长事务编排 事件溯源 + 补偿 Axon Saga / Seata
本地消息表 消息与业务同事务 CAP 内置 手动实现
最大努力通知 异步最终一致 重试 + 回调 重试 + 回调

七、学习建议

7.1 快速上手新语言

从 .NET 到 Java 的学习路径

  1. 语法映射(1-2 周)
    • 类型系统:C# 的 struct → Java 无值类型,全部是引用类型
    • 属性:C# 的 { get; set; } → Java 的 getter/setter 方法
    • 泛型:C# 泛型是具体化的 → Java 泛型是类型擦除的
    • LINQ → Java Stream API(Selectmap, Wherefilter, ToListcollect
  2. 框架映射(2-4 周)
    • ASP.NET Core → Spring Boot(参考第一章)
    • EF Core → Spring Data JPA(参考第二章)
    • xUnit → JUnit 5([Fact]@Test, Assert.EqualAssertions.assertEquals
  3. 生态熟悉(持续)
    • NuGet → Maven / Gradle
    • dotnet climvn / gradle 命令
    • Visual Studio → IntelliJ IDEA

从 Java 到 .NET 的学习路径

  1. 语法映射(1-2 周)
    • Java 的 interface → C# interface(C# 8+ 支持默认实现)
    • Java 的 Stream API → C# LINQ
    • Java 的 Optional<T> → C# 可空引用类型 / T?
    • Java 的 record → C# record(语法几乎相同)
  2. 框架映射(2-4 周)
    • Spring Boot → ASP.NET Core(参考第一章)
    • Spring Data JPA → EF Core(参考第二章)
    • JUnit 5 → xUnit(@Test[Fact]
  3. 生态熟悉(持续)
    • Maven / Gradle → NuGet + dotnet cli
    • IntelliJ IDEA → Visual Studio / Rider

7.2 避免的坑

.NET → Java 常见陷阱

陷阱 说明 解决方案
泛型擦除 Java 泛型在运行时被擦除,无法 typeof(T) 使用 Class<T> 参数传递类型信息
空指针异常 Java 没有可空引用类型编译时检查 使用 Optional@Nullable 注解、防御性编程
值类型缺失 Java 没有 struct,基本类型有装箱开销 理解 8 种基本类型与包装类的区别
属性访问 Java 没有属性语法,必须用 getter/setter 使用 Lombok @Data 减少样板代码
扩展方法 Java 没有扩展方法 使用工具类静态方法或默认接口方法
异步模型 Java 没有 async/await(直到虚拟线程) 使用 CompletableFuture 或虚拟线程
依赖注入 Spring IoC 比 .NET DI 复杂得多 理解 Bean 生命周期、代理机制

Java → .NET 常见陷阱

陷阱 说明 解决方案
值类型语义 C# struct 是值类型,赋值时复制 区分 class(引用)和 struct(值)的使用场景
LINQ 延迟执行 LINQ 查询是延迟执行的,可能多次枚举 使用 .ToList().ToArray() 物化结果
异常处理差异 C# 没有 throws 声明,所有异常都是运行时 阅读文档了解可能抛出的异常
属性与字段 C# 属性本质是方法(get/set) 理解属性在序列化、绑定中的行为
GC 差异 .NET GC 是分代 + 并发的,与 JVM 不同 理解 .NET 内存模型,避免不必要的分配
NuGet 包管理 与 Maven 的依赖解析策略不同 理解 PackageReferencepackages.config

通用陷阱(跨语言)

陷阱 说明 解决方案
过度设计 在新语言中复制旧语言的复杂架构 先学习新语言的最佳实践,再设计架构
忽视生态 只关注语言本身,不了解框架和工具链 花时间学习包管理、构建工具、IDE
直接翻译 逐行翻译代码,忽略语言特性差异 理解语义差异,重新设计实现方式
性能假设 假设两种语言性能特征相同 实际测试,不要凭经验假设

八、学习资源推荐

官方文档

资源 链接 说明
ASP.NET Core 文档 https://learn.microsoft.com/aspnet/core/ .NET Web 开发官方指南
Spring Boot 文档 https://docs.spring.io/spring-boot/docs/current/reference/html/ Spring Boot 官方参考
EF Core 文档 https://learn.microsoft.com/ef/core/ Entity Framework Core 指南
Spring Data JPA 文档 https://docs.spring.io/spring-data/jpa/docs/current/reference/html/ Spring Data JPA 参考
Polly 文档 https://www.pollydocs.org/ .NET 弹性策略库
Resilience4j 文档 https://resilience4j.readme.io/ Java 弹性策略库

书籍推荐

书籍 语言 适合阶段 说明
《C# in Depth》 .NET 进阶 深入理解 C# 语言特性
《Effective Java》 Java 进阶 Java 最佳实践圣经
《Spring in Action》 Java 入门 Spring 框架全面介绍
《ASP.NET Core in Action》 .NET 入门 ASP.NET Core 全面介绍
《微服务架构设计模式》 通用 进阶 跨语言的微服务设计模式
《实现领域驱动设计》 通用 精通 DDD 实践指南
《释放领域驱动设计》 通用 精通 DDD 战略与战术设计

在线课程

平台 课程 说明
Pluralsight ASP.NET Core 路径 .NET 系统学习
Udemy Spring Boot 完整课程 Java 系统学习
Microsoft Learn .NET 学习路径 免费官方学习资源
Spring Guides https://spring.io/guides 免费 Spring 官方教程

社区与工具

资源 链接 说明
Stack Overflow https://stackoverflow.com/ 技术问题问答
GitHub https://github.com/ 开源项目与代码学习
.NET 中文社区 https://www.dotnet.gov.cn/ .NET 中文资源
Spring 中文社区 https://spring.io/ Spring 官方中文站
Rider IDE https://www.jetbrains.com/rider/ 跨平台 .NET IDE
IntelliJ IDEA https://www.jetbrains.com/idea/ Java 开发主力 IDE

实践项目建议

  1. 对照重写:选择一个已知的 .NET 项目,用 Java 重写,对比两种实现
  2. 开源贡献:参与 .NET 或 Java 开源项目,学习最佳实践
  3. 技术博客:记录迁移过程中的发现和踩坑经验
  4. 代码审查:互相审查 .NET 和 Java 代码,发现思维差异
  5. 架构演练:用两种技术栈实现同一架构,对比优劣

总结:跨领域能力迁移的核心不是记住每个框架的 API 对照表,而是理解背后的设计思想和抽象模式。当你掌握了”依赖注入”、”中间件管道”、”事件驱动”、”弹性设计”这些通用概念后,具体使用哪种语言或框架只是实现细节。持续学习、保持开放心态,才能在技术变迁中保持竞争力。