Spring Webflux集成MapStruct后接口出现404错误求助
问题描述
- 集成Swagger-UI的Spring Webflux项目引入MapStruct依赖后,出现接口404,控制台输出404错误,浏览器显示白标错误页面;Swagger UI可正常加载,但测试控制器未在Swagger中显示。移除MapStruct依赖后接口恢复正常。
- 移除启动类
FluxDemoApplication上的@ComponentScan注解,会抛出Mapper Bean未找到的异常。
项目代码
Pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.2.4</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>fluxDemo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>fluxDemo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>21</java.version> <org.mapstruct.version>1.6.0.Beta1</org.mapstruct.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>r2dbc-postgresql</artifactId> <version>1.0.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-r2dbc</artifactId> <version>3.2.4</version> </dependency> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webflux-ui</artifactId> <version>2.5.0</version> </dependency> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>1.6.0.Beta1</version> </dependency> <!-- <dependency>--> <!-- <groupId>org.mapstruct</groupId>--> <!-- <artifactId>mapstruct-processor</artifactId>--> <!-- <version>1.5.5 Final</version>--> <!-- </dependency>--> <!-- <dependency>--> <!-- <groupId>org.projectlombok</groupId>--> <!-- <artifactId>lombok-mapstruct-binding</artifactId>--> <!-- <version>0.2.0</version>--> <!-- </dependency>--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> <!-- <plugin>--> <!-- <groupId>org.apache.maven.plugins</groupId>--> <!-- <artifactId>maven-compiler-plugin</artifactId>--> <!-- <version>3.11.0</version>--> <!-- <configuration>--> <!-- <source>11</source>--> <!-- <target>11</target>--> <!-- <annotationProcessorPaths>--> <!-- --> <!-- <path>--> <!-- <groupId>org.mapstruct</groupId>--> <!-- <artifactId>mapstruct-processor</artifactId>--> <!-- <version>1.5.5.Final</version>--> <!-- </path>--> <!-- --> <!-- </annotationProcessorPaths>--> <!-- </configuration>--> <!-- </plugin>--> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>1.8</source> <target>1.8</target> <annotationProcessorPaths> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.30</version> </path> <path> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${org.mapstruct.version}</version> </path> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok-mapstruct-binding</artifactId> <version>0.2.0</version> </path> </annotationProcessorPaths> </configuration> </plugin> </plugins> </build> </project>
SpringBootApplication.java
import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.info.Info; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ComponentScan; @SpringBootApplication @OpenAPIDefinition(info = @Info(title = "testFlux", version = "0.0.1", description = "test project")) @ComponentScan(basePackages = {"com.example.fluxDemo.Mapper"}) public class FluxDemoApplication { public static void main(String[] args) { SpringApplication.run(FluxDemoApplication.class, args); } }
Mapper Interface
package com.example.fluxDemo.Mapper; import com.example.fluxDemo.Dto.TestModelDto; import com.example.fluxDemo.Model.TestModel; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; import org.springframework.stereotype.Component; @Mapper public interface TestMapper { TestMapper INSTANCE = Mappers.getMapper(TestMapper.class); TestModelDto entityToDto(TestModel testModel); TestModel dtoToEntity(TestModelDto testModelDto); }
TestModelDto类
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.data.annotation.Id; import org.springframework.data.relational.core.mapping.Table; @Data @AllArgsConstructor @NoArgsConstructor public class TestModelDto { private Long id; private String username; private String address; }
TestModel类
package com.example.fluxDemo.Model; import org.springframework.data.annotation.Id; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.data.relational.core.mapping.Table; @Data @AllArgsConstructor @NoArgsConstructor @Table("test_model_db") public class TestModel { @Id private Long id; private String username; private String address; }
ParentController类
package com.example.fluxDemo.Controller; import com.example.fluxDemo.Dto.TestModelDto; import com.example.fluxDemo.Service.ParentService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @RestController @RequestMapping("/test") public class ParentController { @Autowired private ParentService service; @GetMapping("/hello") public ResponseEntity<Mono<String>> test(){ return ResponseEntity.ok().body(Mono.just("Hello..!")); } @GetMapping("/all") public ResponseEntity<Flux<TestModelDto>> allModels(){ return ResponseEntity.ok().body(service.all()); } @GetMapping("/{id}") public ResponseEntity<Mono<TestModelDto>> getModel(@PathVariable Long id){ return ResponseEntity.ok().body(service.getModel(id)); } @PostMapping("/new") public ResponseEntity<Mono<TestModelDto>> newModel(@RequestBody Mono<TestModelDto> testModelDto){ return ResponseEntity.ok().body(service.saveModel(testModelDto)); } @PutMapping("/update") public ResponseEntity<Mono<TestModelDto>> updateModel(@RequestBody Mono<TestModelDto> testModelDtoMono, Long id){ return ResponseEntity.accepted().body(service.updateModel(testModelDtoMono, id)); } @DeleteMapping("/{id}") public Mono<Void> deleteModel(@PathVariable Long id){ return service.deleteById(id); } }
解决方案
1. 修复组件扫描范围
启动类上的@ComponentScan只指定了Mapper包,导致Spring无法扫描到Controller、Service等其他组件,直接引发接口404和Swagger不显示控制器的问题。
修改方案二选一:
- 扩大扫描范围到项目根包:
@SpringBootApplication @OpenAPIDefinition(info = @Info(title = "testFlux", version = "0.0.1", description = "test project")) @ComponentScan(basePackages = {"com.example.fluxDemo"}) public class FluxDemoApplication { public static void main(String[] args) { SpringApplication.run(FluxDemoApplication.class, args); } }
- 直接移除
@ComponentScan:@SpringBootApplication默认会扫描启动类所在包及其所有子包,足以覆盖项目中所有组件。
2. 让MapMapper成为Spring管理的Bean
当前使用Mappers.getMapper()获取实例的方式,不会将Mapper注册到Spring容器,所以移除@ComponentScan会报Bean找不到异常。
修改Mapper接口,指定组件模型为Spring:
package com.example.fluxDemo.Mapper; import com.example.fluxDemo.Dto.TestModelDto; import com.example.fluxDemo.Model.TestModel; import org.mapstruct.Mapper; @Mapper(componentModel = "spring") public interface TestMapper { TestModelDto entityToDto(TestModel testModel); TestModel dtoToEntity(TestModelDto testModelDto); }
之后在Service中直接通过@Autowired注入TestMapper即可,无需再使用静态INSTANCE实例。
3. 统一Java版本配置
pom中java.version设置为21,但maven-compiler-plugin的source和target指定为1.8,版本不匹配会引发编译问题。调整插件配置:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <source>${java.version}</source> <target>${java.version}</target> <annotationProcessorPaths> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.30</version> </path> <path> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${org.mapstruct.version}</version> </path> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok-mapstruct-binding</artifactId> <version>0.2.0</version> </path> </annotationProcessorPaths> </configuration> </plugin>
4. 完善MapStruct依赖
取消注释pom中被注释的mapstruct-processor和lombok-mapstruct-binding依赖,确保编译时注解处理器完整:
<dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${org.mapstruct.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok-mapstruct-binding</artifactId> <version>0.2.0</version> <scope>provided</scope> </dependency>
内容的提问来源于stack exchange,提问作者Arul NR
相关产品推荐
相关产品推荐

