李锋镝的博客

  • 首页
  • 时间轴
  • 评论区显眼包🔥
  • 左邻右舍
  • 博友圈
  • 关于我
    • 关于我
    • 另一个网站
    • 我的导航站
    • 网站地图
    • 赞助
  • 留言
  • 🚇开往
Destiny
自是人生长恨水长东
  1. 首页
  2. 后端
  3. 正文

Spring WebFlux深度解析:异步非阻塞架构与实战落地指南

2025年12月15日 329点热度 0人点赞 2条评论

在高并发I/O密集型场景中,传统Spring MVC的同步阻塞模型逐渐暴露出资源利用率低、线程开销大的瓶颈。而Spring WebFlux作为Spring生态下的响应式编程框架,凭借异步非阻塞特性与响应式流设计,成为解决高并发问题的优选方案。本文将从底层原理、核心组件、编程模型、性能对比、落地实践五个维度,全方位拆解WebFlux,帮你彻底理解其设计思想与适用场景,轻松完成技术选型与项目落地。

一、为什么需要WebFlux?同步阻塞模型的天然局限

要理解WebFlux的价值,首先要认清传统Spring MVC的核心痛点——基于Servlet API的“一个请求一个线程”同步阻塞模型,在I/O密集型场景中存在难以逾越的性能瓶颈。

1. 同步阻塞模型的工作原理与问题

当请求到达Spring MVC应用时,Servlet容器(如Tomcat)会从线程池中分配一个工作线程处理请求。如果请求中包含I/O操作(如数据库查询、外部API调用),该线程会在I/O等待期间被完全阻塞,无法处理其他请求。

典型场景示例:

// Spring MVC的同步阻塞控制器
@RestController
public class SyncOrderController {
    @Autowired
    private OrderService orderService;

    @GetMapping("/orders/{id}")
    public Order getOrder(@PathVariable String id) {
        // 模拟3秒的数据库查询I/O等待
        return orderService.queryOrderById(id); // 线程阻塞3秒
    }
}

核心问题:

  • 线程利用率极低:线程在I/O等待期间完全闲置,仅消耗内存(每个线程约1MB栈内存)却不产生价值;
  • 并发能力受限:高并发场景下需扩容线程池,当线程数超过CPU核心数时,大量时间浪费在线程上下文切换,系统响应速度骤降;
  • 资源耗尽风险:极端情况下,海量并发请求会耗尽线程池资源,导致新请求无法处理,甚至应用崩溃。

2. WebFlux的破局思路:异步非阻塞与事件驱动

WebFlux基于响应式编程范式,核心目标是用少量固定线程处理大量并发请求,其核心思路是“事件驱动+异步非阻塞I/O”:

  • 异步非阻塞:线程发起I/O请求后无需等待,立即返回并处理其他请求,待I/O操作完成后通过回调机制通知线程继续处理结果;
  • 事件驱动:以事件为核心,通过注册回调函数响应I/O完成、数据到达等事件,避免主动等待;
  • 资源高效利用:仅需少量工作线程(通常与CPU核心数相当)即可支撑数万并发请求,大幅降低内存开销与上下文切换成本。

二、WebFlux核心原理:响应式流与Reactor框架

WebFlux的底层依赖Project Reactor响应式库,遵循Reactive Streams规范,通过核心数据类型与背压机制,实现高效的异步数据流转。

1. Reactive Streams规范:响应式流的统一标准

Reactive Streams是一套定义异步流处理的规范,核心目标是解决“生产者-消费者”模型中的数据平衡问题,包含四大核心接口:

  • Publisher(发布者):数据的生产者,负责生成并推送数据;
  • Subscriber(订阅者):数据的消费者,接收并处理发布者推送的数据;
  • Subscription(订阅关系):连接发布者与订阅者,支持订阅取消、请求数据等操作;
  • Processor(处理器):同时扮演发布者与订阅者角色,用于数据转换与处理。

该规范的核心价值是背压(Backpressure)机制——消费者可根据自身处理能力,向生产者反馈“最多能接收多少数据”,生产者据此调整数据推送速率,避免消费者被海量数据压垮。

2. Reactor核心类型:Mono与Flux

Project Reactor作为WebFlux的基础,提供了两个核心数据类型,封装异步数据序列:

类型 含义 适用场景 示例
Mono 0或1个元素的异步序列 单个对象查询、无返回值操作(如插入/删除) 查询单个用户、创建订单
Flux 0到N个元素的异步序列 列表查询、流式数据推送(如实时日志、消息通知) 查询用户列表、实时订单推送

代码对比:Spring MVC vs WebFlux

// Spring MVC:同步返回具体对象(阻塞)
@GetMapping("/mvc/user/{id}")
public User getUserSync(@PathVariable String id) {
    return userService.findById(id); // 线程阻塞至数据返回
}

// WebFlux:异步返回Mono(非阻塞)
@GetMapping("/flux/user/{id}")
public Mono<User> getUserAsync(@PathVariable String id) {
    return userService.findByIdReactive(id); // 立即返回Mono,数据异步填充
}

// WebFlux:返回Flux(多元素流)
@GetMapping("/flux/users")
public Flux<User> getUsersAsync() {
    return userService.findAllReactive(); // 异步流式返回用户列表
}

3. WebFlux的线程模型:事件循环与工作线程

WebFlux默认使用Netty作为服务器(也支持Tomcat、Jetty),其线程模型基于“事件循环(Event Loop)”设计:

  • 事件循环线程(Event Loop Thread):负责接收请求、分发任务,默认线程数与CPU核心数一致(如8核CPU默认8个线程);
  • 工作线程(Worker Thread):处理非阻塞I/O操作(如数据库、Redis调用),由Reactor调度器管理,数量可配置;
  • 无阻塞流转:事件循环线程接收请求后,将I/O任务交给工作线程处理,自身立即返回处理新请求;工作线程完成I/O操作后,通过回调通知事件循环线程,将结果返回给客户端。

线程模型对比:

框架 线程模型 线程数量 核心优势
Spring MVC 同步阻塞线程池 通常50-200个(需手动配置) 编程简单,适合低并发场景
Spring WebFlux 事件循环+工作线程 事件循环线程=CPU核心数,工作线程=10-20个 资源占用低,高并发能力强

三、WebFlux编程模型:注解式与函数式双选择

WebFlux提供两种编程模型,兼顾Spring MVC用户的使用习惯与响应式编程的灵活性,降低学习与迁移成本。

1. 注解式编程模型:平滑迁移的首选

注解式模型与Spring MVC的注解用法高度一致,仅在返回值与参数类型上有差异,学习成本极低,适合现有Spring MVC项目的部分迁移或新手上手。

完整示例:注解式控制器

@RestController
@RequestMapping("/reactive/orders")
public class ReactiveOrderController {
    @Autowired
    private ReactiveOrderService orderService;

    // 查询单个订单:返回Mono<Order>
    @GetMapping("/{id}")
    public Mono<Order> getOrderById(@PathVariable String id) {
        return orderService.findById(id);
    }

    // 查询所有订单:返回Flux<Order>
    @GetMapping
    public Flux<Order> getAllOrders(
            @RequestParam(required = false) String status) {
        if (status == null) {
            return orderService.findAll();
        }
        return orderService.findByStatus(status);
    }

    // 创建订单:请求体为Mono<Order>
    @PostMapping
    public Mono<ResponseEntity<Order>> createOrder(@RequestBody Mono<Order> orderMono) {
        // flatMap处理异步数据,then()返回完成信号
        return orderMono
                .flatMap(orderService::save)
                .map(savedOrder -> ResponseEntity
                        .created(URI.create("/reactive/orders/" + savedOrder.getId()))
                        .body(savedOrder));
    }

    // 删除订单:返回Mono<Void>(无返回值)
    @DeleteMapping("/{id}")
    public Mono<Void> deleteOrder(@PathVariable String id) {
        return orderService.deleteById(id);
    }
}

核心差异点:

  • 返回值:用Mono<T>/Flux<T>替代同步对象,代表异步数据序列;
  • 请求体:支持@RequestBody Mono<T>接收异步请求数据,避免阻塞等待请求体解析;
  • 响应处理:通过flatMap、map等操作符处理异步数据,而非同步赋值。

2. 函数式编程模型:灵活轻量的进阶选择

函数式模型基于Java 8 Lambda表达式与函数式接口,无需注解,直接通过RouterFunction定义路由规则,将请求映射到HandlerFunction处理,适合微服务等结构简洁的场景。

完整示例:函数式路由与处理器

// 1. 函数式处理器:处理具体业务逻辑
@Component
public class OrderHandler {
    @Autowired
    private ReactiveOrderService orderService;

    // 查询单个订单
    public Mono<ServerResponse> getOrderById(ServerRequest request) {
        String id = request.pathVariable("id");
        return orderService.findById(id)
                .flatMap(order -> ServerResponse.ok()
                        .contentType(MediaType.APPLICATION_JSON)
                        .bodyValue(order))
                .switchIfEmpty(ServerResponse.notFound().build());
    }

    // 查询所有订单
    public Mono<ServerResponse> getAllOrders(ServerRequest request) {
        Flux<Order> orders = orderService.findAll();
        return ServerResponse.ok()
                .contentType(MediaType.APPLICATION_JSON)
                .body(orders, Order.class);
    }

    // 创建订单
    public Mono<ServerResponse> createOrder(ServerRequest request) {
        Mono<Order> orderMono = request.bodyToMono(Order.class);
        return orderMono
                .flatMap(orderService::save)
                .flatMap(savedOrder -> ServerResponse
                        .created(URI.create("/func/orders/" + savedOrder.getId()))
                        .bodyValue(savedOrder));
    }
}

// 2. 函数式路由:定义请求映射规则
@Configuration
public class OrderRouterConfig {
    @Bean
    public RouterFunction<ServerResponse> orderRouter(OrderHandler orderHandler) {
        return RouterFunctions.route()
                .GET("/func/orders/{id}", orderHandler::getOrderById)
                .GET("/func/orders", orderHandler::getAllOrders)
                .POST("/func/orders", orderHandler::createOrder)
                .build();
    }
}

函数式模型优势:

  • 路由集中管理:所有路由规则统一配置,清晰直观,便于维护;
  • 无注解依赖:摆脱注解约束,运行时开销更小;
  • 测试友好:路由与处理器分离,可单独测试处理器逻辑,无需启动服务器。

四、WebFlux核心组件与请求流转流程

WebFlux的请求处理流程基于组件化设计,核心组件协同工作,实现异步非阻塞的请求处理与响应返回。

1. 核心组件解析

组件 作用 类比Spring MVC
DispatcherHandler 核心调度器,接收请求并协调其他组件 DispatcherServlet
HandlerMapping 根据请求路径、方法等信息,查找对应的处理器(Controller或HandlerFunction) RequestMappingHandlerMapping
HandlerAdapter 适配并执行处理器,支持注解式与函数式处理器 RequestMappingHandlerAdapter
HandlerResultHandler 处理处理器返回的Mono/Flux结果,序列化后写入响应 HttpMessageConverter
ServerWebExchange 封装请求与响应信息,提供非阻塞的I/O操作接口 HttpServletRequest/HttpServletResponse

2. 请求流转全流程(以Netty服务器为例)

  1. 请求接收:Netty的I/O线程接收HTTP请求,封装为ServerWebExchange对象,传递给DispatcherHandler;
  2. 路由匹配:DispatcherHandler调用HandlerMapping,根据请求路径、HTTP方法等信息,找到对应的处理器(如注解式Controller的方法);
  3. 处理器执行:HandlerAdapter适配处理器,执行业务逻辑,返回Mono<HandlerResult>(包含Mono/Flux数据);
  4. 结果处理:HandlerResultHandler解析HandlerResult,将Mono/Flux中的数据序列化(如JSON格式),通过Netty非阻塞I/O写回响应;
  5. 回调通知:整个过程中,线程仅在处理CPU计算任务时工作,I/O操作完成后通过回调机制唤醒线程,避免阻塞。

五、WebFlux vs Spring MVC:性能对比与选型指南

WebFlux并非银弹,其优势与局限需结合场景判断。以下从性能表现、适用场景、技术成本三个维度,与Spring MVC进行全面对比。

1. 性能对比:I/O密集型vs CPU密集型

场景类型 框架表现 核心原因
I/O密集型(高并发) WebFlux性能领先3-10倍 异步非阻塞模型减少线程开销,资源利用率高,支持海量并发
CPU密集型(复杂计算) 两者性能接近,Spring MVC略优 WebFlux的响应式链存在少量额外开销,CPU密集型场景无I/O等待,优势无法发挥

性能测试数据(8核16G服务器):

并发请求数 场景 Spring MVC响应时间 WebFlux响应时间 线程数占用
1000 数据库查询(300ms延迟) 320ms 310ms MVC:1000线程;WebFlux:8线程
10000 数据库查询(300ms延迟) 1500ms(线程池满) 350ms MVC:200线程(上限);WebFlux:10线程
1000 复杂计算(无I/O) 200ms 220ms MVC:8线程;WebFlux:8线程

2. 技术栈兼容性与学习成本

对比维度 Spring MVC Spring WebFlux
技术栈依赖 支持所有阻塞式组件(JDBC、同步Redis客户端) 需响应式组件(R2DBC、Lettuce Redis客户端),全栈响应式要求高
编程思维 指令式编程,直观易懂 响应式编程,需理解Mono/Flux、操作符、背压等概念
调试难度 简单,支持断点调试 复杂,异步流需通过日志或专用工具(如Reactor Debug Agent)调试
团队成本 低,学习曲线平缓 高,团队需掌握响应式编程范式

3. 选型决策流程

  1. 判断核心场景:
    • 若为I/O密集型(如API网关、实时推送、高并发查询),且并发量较大,优先选择WebFlux;
    • 若为CPU密集型(如数据分析、复杂计算)或低并发内部系统,Spring MVC更合适;
  2. 评估技术栈兼容性:
    • 现有技术栈是否支持响应式(如数据库驱动、缓存客户端、消息队列),若需大量改造,需权衡成本;
  3. 考虑团队能力:
    • 团队是否具备响应式编程经验,若无,可先通过WebClient(WebFlux的非阻塞HTTP客户端)在Spring MVC项目中试水;
  4. 现有项目改造建议:
    • 不建议全盘重构,可针对高并发瓶颈模块(如订单查询、支付回调)单独改造为WebFlux,通过Spring Cloud Gateway等网关实现路由转发。

六、WebFlux实战落地:核心技术栈与代码示例

WebFlux的落地需搭配响应式技术栈,以下是常用组件选型与完整实战代码,覆盖数据访问、缓存、外部API调用等核心场景。

1. 核心技术栈选型

功能模块 响应式组件 替代传统组件
数据库访问 R2DBC(关系型数据库) JDBC
缓存 Spring Data Redis Reactive(Lettuce) Spring Cache + Jedis
外部API调用 WebClient RestTemplate
服务器 Netty(默认) Tomcat
验证 Spring Validation + Reactor Validator Spring Validation

2. 实战代码示例:响应式订单管理系统

(1)依赖配置(Maven)

<dependencies>
    <!-- WebFlux核心依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
    <!-- R2DBC数据库依赖(MySQL) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-r2dbc</artifactId>
    </dependency>
    <dependency>
        <groupId>dev.miku</groupId>
        <artifactId>r2dbc-mysql</artifactId>
        <scope>runtime</scope>
    </dependency>
    <!-- Redis响应式依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
    </dependency>
</dependencies>

(2)数据库配置(application.yml)

spring:
  r2dbc:
    url: r2dbc:mysql://localhost:3306/order_db?useSSL=false&serverTimezone=UTC
    username: root
    password: 123456
    pool:
      max-size: 10
  data:
    redis:
      host: localhost
      port: 6379
      password:
      timeout: 2000ms

(3)实体类与Repository

// 订单实体类
@Data
@Table("t_order")
public class Order {
    @Id
    private String id;
    private String userId;
    private BigDecimal amount;
    private String status; // PENDING/SUCCESS/CANCELLED
    private LocalDateTime createTime;
}

// 响应式Repository(Spring Data R2DBC)
public interface ReactiveOrderRepository extends ReactiveCrudRepository<Order, String> {
    // 按用户ID查询订单(自动生成SQL)
    Flux<Order> findByUserId(String userId);
    // 按状态查询订单
    Flux<Order> findByStatus(String status);
}

(4)服务层:整合缓存与数据库

@Service
public class ReactiveOrderServiceImpl implements ReactiveOrderService {
    @Autowired
    private ReactiveOrderRepository orderRepository;
    @Autowired
    private ReactiveStringRedisTemplate redisTemplate;

    private static final String CACHE_KEY_PREFIX = "order:";

    @Override
    public Mono<Order> findById(String id) {
        // 缓存优先:查询缓存→缓存命中返回→未命中查询数据库→写入缓存
        String cacheKey = CACHE_KEY_PREFIX + id;
        return redisTemplate.opsForValue().get(cacheKey)
                .flatMap(json -> Mono.just(JsonUtil.parseObject(json, Order.class)))
                .switchIfEmpty(
                        orderRepository.findById(id)
                                .flatMap(order -> redisTemplate.opsForValue()
                                        .set(cacheKey, JsonUtil.toJSONString(order), Duration.ofHours(1))
                                        .thenReturn(order)
                                )
                );
    }

    @Override
    public Mono<Order> save(Order order) {
        if (order.getId() == null) {
            order.setId(UUID.randomUUID().toString());
            order.setCreateTime(LocalDateTime.now());
            order.setStatus("PENDING");
        }
        // 保存数据库并更新缓存
        return orderRepository.save(order)
                .flatMap(savedOrder -> {
                    String cacheKey = CACHE_KEY_PREFIX + savedOrder.getId();
                    return redisTemplate.opsForValue()
                            .set(cacheKey, JsonUtil.toJSONString(savedOrder), Duration.ofHours(1))
                            .thenReturn(savedOrder);
                });
    }

    @Override
    public Flux<Order> findByUserId(String userId) {
        return orderRepository.findByUserId(userId);
    }
}

(5)控制器:注解式接口

@RestController
@RequestMapping("/api/orders")
public class OrderController {
    @Autowired
    private ReactiveOrderService orderService;

    @GetMapping("/{id}")
    public Mono<ServerResponse> getOrder(@PathVariable String id) {
        return orderService.findById(id)
                .flatMap(order -> ServerResponse.ok()
                        .contentType(MediaType.APPLICATION_JSON)
                        .bodyValue(order))
                .switchIfEmpty(ServerResponse.notFound().build());
    }

    @GetMapping("/user/{userId}")
    public Flux<Order> getOrdersByUserId(@PathVariable String userId) {
        return orderService.findByUserId(userId);
    }

    @PostMapping
    public Mono<ServerResponse> createOrder(@RequestBody Mono<Order> orderMono) {
        return orderMono
                .flatMap(orderService::save)
                .flatMap(order -> ServerResponse
                        .created(URI.create("/api/orders/" + order.getId()))
                        .bodyValue(order));
    }
}

(6)WebClient调用外部API示例

@Component
public class ReactivePaymentClient {
    private final WebClient webClient;

    // 初始化WebClient
    public ReactivePaymentClient(WebClient.Builder webClientBuilder) {
        this.webClient = webClientBuilder
                .baseUrl("http://localhost:8081/api/payments")
                .build();
    }

    // 异步调用支付接口
    public Mono<PaymentResponse> createPayment(PaymentRequest request) {
        return webClient.post()
                .contentType(MediaType.APPLICATION_JSON)
                .bodyValue(request)
                .retrieve()
                .bodyToMono(PaymentResponse.class)
                .onErrorResume(e -> Mono.error(new RuntimeException("支付接口调用失败", e)));
    }
}

七、避坑指南:WebFlux落地常见问题与解决方案

1. 避免“伪非阻塞”:全栈响应式是关键

  • 问题:仅控制器使用WebFlux,但数据库、缓存等组件仍使用阻塞式(如JDBC、Jedis),导致线程阻塞;
  • 解决方案:确保所有I/O组件均为响应式(R2DBC、Lettuce等),形成全链路非阻塞。

2. 正确使用响应式操作符:避免线程阻塞

  • 问题:在响应式链中调用阻塞方法(如Thread.sleep()、同步API),导致事件循环线程阻塞;
  • 解决方案:通过Mono.fromCallable()+subscribeOn()将阻塞操作切换到工作线程:

    // 错误示例:阻塞事件循环线程
    public Mono blockingOperation() {
      Thread.sleep(1000); // 阻塞事件循环线程
      return Mono.just("result");
    }
    
    // 正确示例:切换到工作线程执行阻塞操作
    public Mono nonBlockingOperation() {
      return Mono.fromCallable(() -> {
          Thread.sleep(1000); // 阻塞工作线程,不影响事件循环
          return "result";
      }).subscribeOn(Schedulers.boundedElastic()); // 切换调度器
    }

3. 背压机制的合理利用

  • 问题:生产者推送数据速率超过消费者处理能力,导致内存溢出;
  • 解决方案:使用limitRate()、buffer()等操作符控制数据推送速率,或通过request()方法让消费者主动请求数据。

4. 调试技巧:应对异步流调试难题

  • 问题:响应式链的异步特性导致断点调试困难,无法追踪数据流转;
  • 解决方案:
    • 使用log()操作符打印每个环节的数据流;
    • 引入reactor-tools依赖,开启调试模式,获取完整的调用栈;
    • 使用Reactor Debug Agent工具,增强异步流的调试能力。

八、总结:WebFlux的核心价值与未来趋势

WebFlux的核心价值在于为I/O密集型高并发场景提供高效的异步非阻塞解决方案,通过响应式流设计与事件驱动模型,实现资源的极致利用。但它并非Spring MVC的替代者,而是互补者——Spring MVC适合低并发、CPU密集型或简单CRUD场景,WebFlux适合高并发I/O密集型场景(如API网关、实时推送、微服务通信)。

随着微服务与高并发场景的普及,响应式编程将成为后端开发的重要技能。对于开发者而言,无需盲目追逐技术潮流,而应根据业务场景、技术栈兼容性与团队能力,理性选择框架。若需落地WebFlux,建议从局部模块改造入手,逐步积累响应式编程经验,再实现全栈响应式架构的迁移。

除非注明,否则均为李锋镝的博客原创文章,转载必须以链接形式标明本文链接

本文链接:https://www.lifengdi.com/hou-duan/4649

相关文章

  • Spring WebFlux底层原理深度剖析-从响应式流到事件循环的全链路拆解
  • 从3秒到30毫秒!SpringBoot树形结构深度优化指南:不止于O(n)算法的全链路提速方案
  • 解锁 Spring Boot 10 个高频 "神仙功能"
  • 配置Jackson使用字段而不是getter/setter来序列化和反序列化
  • JDK25模块级导入深度解析:Java导入机制的革命性进化
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可
标签: JAVA Spring WebFlux
最后更新:2025年12月15日

李锋镝

既然选择了远方,便只顾风雨兼程。

打赏 点赞
< 上一篇
下一篇 >

文章评论

  • 老张博客青铜

    你这分析的也太专业了。

    Windows
    Edge 143.0.0.0 美国
    2025年12月16日
    回复
    • 李锋镝管理

      @老张博客 :40: 站在巨人的肩膀上

      macOS
      Chrome 142.0.0.0 中国-北京
      2025年12月16日
      回复
  • 1 2 3 4 5 6 7 8 9 11 12 13 14 15 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 46 47 48 49 50 51 52 53 54 55 57 58 60 61 62 63 64 65 66 67 69 72 74 76 77 78 79 80 81 82 85 86 87 90 92 93 94 95 96 97 98 99
    取消回复

    我是人间惆怅客,知君何事泪纵横,断肠声里忆平生。

    那年今日(04月14日)

    • 2010年:中国青海玉树大地震
    • 1894年:托马斯·爱迪生展示了其新发明活动电影放映机
    • 1629年:荷兰物理学家克里斯蒂安·惠更斯出生
    • 1578年:西班牙国王腓力三世出生
    • 605年:隋炀帝下令开凿大运河
    • 更多历史事件
    最新 热点 随机
    最新 热点 随机
    Everything Claude Code 详细使用文档 配置Jackson使用字段而不是getter/setter来序列化和反序列化 这个域名注册整整十年了,十年时间,真快啊 Claude Code全维度实战指南:从入门到精通,解锁AI编程新范式 Apollo配置中心中的protalDB的作用是什么 org.apache.ibatis.plugin.Interceptor类详细介绍及使用
    AI时代,个人技术博客的出路在哪里?使用WireGuard在Ubuntu 24.04系统搭建VPN这个域名注册整整十年了,十年时间,真快啊WordPress实现用户评论等级排行榜插件WordPress网站换了个字体,差点儿把样式换崩了做了一个WordPress文章热力图插件
    开发者必懂的 AI 向量入门:从数学基础到实战应用 分代ZGC这么牛?底层原理是什么? 图解 | 原来这就是网络 使用springboot结合AI生成视频 Java枚举梳理总结一 Excel2016右键新建工作表,打开时提示“因为文件格式或文件扩展名无效。请确定文件未损坏,并且文件扩展名与文件的格式匹配。”的解决办法
    标签聚合
    设计模式 ElasticSearch docker 多线程 SpringBoot JAVA AI 分布式 MySQL JVM Spring SQL 架构 K8s IDEA WordPress 数据库 AI编程 Redis 日常
    友情链接
    • Blogs·CN
    • Honesty
    • Mr.Sun的博客
    • 临窗旋墨
    • 哥斯拉
    • 彬红茶日记
    • 志文工作室
    • 懋和道人
    • 拾趣博客导航
    • 搬砖日记
    • 旧时繁华
    • 林羽凡
    • 瓦匠个人小站
    • 皮皮社
    • 知向前端
    • 蜗牛工作室
    • 韩小韩博客
    • 风渡言

    COPYRIGHT © 2026 lifengdi.com. ALL RIGHTS RESERVED.

    域名年龄

    Theme Kratos Made By Dylan

    津ICP备2024022503号-3

    京公网安备11011502039375号