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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 02:45:56