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">© 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))); } }
额外注意事项
- 路由冲突:
EndPointConfig中的RouterFunction和VideoController的@GetMapping("video/{title}")都映射了同一个路径,会导致请求处理冲突,建议保留其中一种方式(比如删除RouterFunction部分,因为Controller已经实现了相同的路由)。 - 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
相关产品推荐
相关产品推荐

