spring-bootmavenspring-mvccustomization

How to override REST Service in springboot


I have a project named customer written in spring boot, and another maven project named customer-customization adds the customer project as a dependency and uses it. My goal: To change the rest service. As the name of the project suggests, I want to allow the project to be used by making changes without touching my source code.

Customer project

Spring boot version : 3.2.2

Java version: 21

pom.xml

<dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

CityService.java

@RequestMapping("city")
public interface CityService {

    @PostMapping(value = "/createCity", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
    ResponseEntity<CityDto> createCity(@RequestBody CityDto param);

}

CityServiceImpl.java

@RestController
public class CityServiceImpl implements CityService {

    @Override
    public ResponseEntity<CityDto> createCity(CityDto param) {
        ...
    }

}

customer-customization project

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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.rz.customer</groupId>
    <artifactId>customer-customization</artifactId>
    <packaging>jar</packaging>
    <version>0.0.1-alpha.1</version>
    <name>Customer Customization</name>
    <properties>
        <start-class>com.rz.customer.app.CustomerCustomizationApplication</start-class>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <customer.version>0.0.1-alpha.1</customer.version>
    </properties>
    <dependencies>

        <dependency>
            <groupId>com.rz.customer</groupId>
            <artifactId>customer-app</artifactId>
            <version>${customer.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>3.2.2</version>
                <configuration>
                    <mainClass>com.rz.customer.app.CustomerCustomizationApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

CustomCityService.java

@RequestMapping("city")
public interface CustomCityService {

    @PostMapping(value = "/createCity", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
    ResponseEntity<CustomCityDto> createCity(@RequestBody CustomCityDto param);
}

CustomCityServiceImpl.java

@RestController
public class CustomCityServiceImpl implements CustomCityService {

    @Override
    public ResponseEntity<CityDto> createCity(CustomCityDto param) {
        ...
    }

}

While running the customer-customization project I'm getting this error:

"Caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'customCityServiceImpl' method 
com.rz.customer.controller.impl.CustomCityServiceImpl#createCity(CityInputDto)
to {POST [/city/createCity], consumes [application/json], produces [application/json]}: There is already 'customCityServiceImpl' bean method
com.rz.customer.controller.impl.CustomCityServiceImpl#createCity(CustomCityDto)"

I know this error is normal because there cannot be more than one service belonging to the same url.

My goal is somehow I want the customer-customization project to overwhelm the service in the customer project. I tried extending the RequestMappingHandlerMapping class but I couldn't. Is it possible to do this, perhaps by adding notation like @Primary to the CustomCityService.java class and prioritizing it, or some other way? Since my goal is to have the customer project done without touching the source code, I want to make this change in the customer-customization project.

How can I do?


Solution

  • I solved my problem with registerHandlerMethod method of the RequestMappingHandlerMapping class.

    @Configuration
    public class CustomRequestMappingHandlerMapping implements WebMvcRegistrations {
    
        @Override
        public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
            return new RequestMappingHandlerMapping() {
    
                @Override
                protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
                    Set<RequestMethod> httpMethods = mapping.getMethodsCondition().getMethods();
                    if (handler.toString().equals("cityServiceImpl") && httpMethods.contains(RequestMethod.POST)
                            && mapping.getPatternValues().contains("/city/createCity")) {
                        return;
                    }
                    super.registerHandlerMethod(handler, method, mapping);
                }
            };
        }
    }