You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot WebFlux视频流getVideo方法抛出NullPointerException求助

视频流应用NullPointerException问题排查

按照Spring Boot WebFlux视频流教程搭建应用,resources目录下已创建videos子文件夹并放入MP4文件。访问http://localhost:8082/show后播放器正常显示,但点击播放触发500错误,报错指向StreamingService类的getVideo方法中的resourceLoader为空。

相关代码片段

StreamingService类

package net.javaguides.springboot.implementations;

import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;

import reactor.core.publisher.Mono;

@Service
public class StreamingService {

  private static final String FORMAT = "classpath:videos/%s.mp4";
  private ResourceLoader resourceLoader;

  public Mono<Resource> getVideo(String title) {
    return Mono.fromSupplier(() -> resourceLoader.getResource(String.format(FORMAT, title)));
  }
}

EndPointConfig配置类

package net.javaguides.springboot.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;

import net.javaguides.springboot.implementations.StreamingService;
import reactor.core.publisher.Mono;

@Configuration
public class EndPointConfig {
  @Autowired
  private StreamingService service;

  @Bean
  public RouterFunction<ServerResponse> router() {
    return RouterFunctions.route().GET("video/{title}", this::videoHandler).build();
  }

  private Mono<ServerResponse> videoHandler(ServerRequest serverRequest) {
    String title = serverRequest.pathVariable("title");
    return ServerResponse.ok().contentType(MediaType.valueOf("video/mp4")).body(service.getVideo(title),
        Resource.class);
  }
}

VideoController控制器

@Controller
@AllArgsConstructor
public class VideoController {
private StreamingService streamingService;

@GetMapping(value = "video/{title}", produces = "video/mp4")
  @ResponseBody
  public Mono<Resource> getVideos(@PathVariable String title, @RequestHeader("Range") String range) {
    System.out.println("Range in bytes = " + range);
    return streamingService.getVideo(title);
  }

@GetMapping("show")
  public String show() {
    return "video";
  }
}

HTML播放器页面

<!DOCTYPE html>
<html
  lang="en"
  xmlns:th="http://www.thymeleaf.org"
  xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity5"
  xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
  layout:decorate="~{layout_navigation.html}">
<head>
<meta charset="UTF-8">
<title>OBR-VIDEOTHEQUE</title>
<meta
  name="viewport"
  content="width=device-width, initial-scale=1.0">
<!--    <link rel="stylesheet" href="styles.css"> -->
<style type="text/css">
#video-player {
    display: none;
}

#video-form {
    width: 60%;
}

.error {
    color: red;
}

.success {
    color: green;
}
</style>
</head>
<body>
  <section layout:fragment="content">
    <meta charset="utf-8">
    
      <div class="container mt-5">
      <h2>Video streaming</h2>
        <video src="video/movie" type="video/mp4" width="720" height="480" controls preload="none"> 
          
        </video>
      </div>
    
  </section>
  <section layout:fragment="footer">
    <footer class="container py-5 text-center footer">
      <div class="row">
        <div class="col-md-12">
          <p class="text-muted">&copy; 2022. Tous
            droits réservés.</p>
        </div>
      </div>
    </footer>
  </section>
  <section layout:fragment="scripts">
    <script th:src="@{/js/jQuery-min.js}"></script>
    <script
      type="text/javascript"
      th:src="@{/js/dropdown.js}"></script>
    <script th:src="@{/js/bootstrap.bundle.js}"></script>
    <script th:src="@{/js/bootstrap.min.js}"></script>
    <script th:src="@{/js/select2.min.js}"></script>
    <!--     <script th:src="@{/js/main.js}"></script> -->
  </section>
</body>
</html>

错误信息

java.lang.NullPointerException: null
    at net.javaguides.springboot.implementations.StreamingService.lambda$0(StreamingService.java:17)

问题原因

StreamingService类中的resourceLoader字段没有被Spring容器注入,导致调用resourceLoader.getResource()时触发空指针异常。当前代码仅声明了resourceLoader变量,但未通过任何注入方式让Spring为其赋值。

修复方案

方案1:构造注入(推荐,符合Spring最佳实践)

修改StreamingService,通过构造函数注入ResourceLoader:

@Service
public class StreamingService {

  private static final String FORMAT = "classpath:videos/%s.mp4";
  private final ResourceLoader resourceLoader;

  // 构造函数注入
  public StreamingService(ResourceLoader resourceLoader) {
    this.resourceLoader = resourceLoader;
  }

  public Mono<Resource> getVideo(String title) {
    return Mono.fromSupplier(() -> resourceLoader.getResource(String.format(FORMAT, title)));
  }
}

方案2:字段注入(简单但不推荐)

在resourceLoader字段上添加@Autowired注解:

@Service
public class StreamingService {

  private static final String FORMAT = "classpath:videos/%s.mp4";
  @Autowired
  private ResourceLoader resourceLoader;

  public Mono<Resource> getVideo(String title) {
    return Mono.fromSupplier(() -> resourceLoader.getResource(String.format(FORMAT, title)));
  }
}

额外注意事项

  1. 路由冲突:EndPointConfig中的RouterFunction和VideoController的@GetMapping("video/{title}")都映射了同一个路径,会导致请求处理冲突,建议保留其中一种方式(比如删除RouterFunction部分,因为Controller已经实现了相同的路由)。
  2. Range头必填问题:VideoController的getVideos方法中@RequestHeader("Range")是必填的,但浏览器第一次请求视频时可能不会携带Range头,会触发MissingRequestHeaderException。需要将其改为可选:
public Mono<Resource> getVideos(@PathVariable String title, @RequestHeader(value = "Range", required = false) String range) {
    if (range != null) {
        System.out.println("Range in bytes = " + range);
    }
    return streamingService.getVideo(title);
}

内容的提问来源于stack exchange,提问作者dbarasuk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 20:25:18