Spring MVC uses a WebBindingInitializer to initialize a WebDataBinder for a given request. We can use ConfigurableWebBindingInitializer to initialize a custom WebBindingInitializer.
In following example, we will register a custom PropertyEditor globally via ConfigurableWebBindingInitializer. To accomplish that, we will not use @EnableWebMvc annotation, instead we will use a plain @Configuration class which will extend WebMvcConfigurationSupport directly, we will then override getConfigurableWebBindingInitializer() method.
From @EnableWebMvc docs:
If WebMvcConfigurer does not expose some more advanced setting that needs to be configured, consider removing the @EnableWebMvc annotation and extending directly from WebMvcConfigurationSupport or DelegatingWebMvcConfiguration
Note that ConfigurableWebBindingInitializer is used by RequestMappingHandlerAdapter to apply data conversion, formatting and validation for the requests.
Example
Spring Controller
package com.logicbig.example;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Date;
@Controller
@RequestMapping("/trade")
public class TradeController {
@GetMapping
@ResponseBody
public String handleRequest(@RequestParam("tradeDate") Date tradeDate) {
return "request received for " + tradeDate;
}
}
Java Config class
@Configuration
@ComponentScan
public class MyWebConfig extends WebMvcConfigurationSupport {
@Override
protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer(
FormattingConversionService mvcConversionService,
Validator mvcValidator) {
ConfigurableWebBindingInitializer initializer =
super.getConfigurableWebBindingInitializer(mvcConversionService,
mvcValidator);
initializer.setPropertyEditorRegistrar(propertyEditorRegistry -> {
SimpleDateFormat dateFormatter =
new SimpleDateFormat("yyyy-MM-dd");
propertyEditorRegistry.registerCustomEditor(
Date.class, new CustomDateEditor(dateFormatter,
true));
});
return initializer;
}
}
In above example, we are not completely replacing ConfigurableWebBindingInitializer, but rather customizing it to use a custom PropertyEditor globally.
Running Example
To try examples, run embedded Jetty (configured in pom.xml of example project below):
mvn jetty:run
$ curl -s http://localhost:8080/trade?tradeDate=2017-12-27 request received for Wed Dec 27 00:00:00 CST 2017
Integration Test
package com.logicbig.example;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig;
import org.springframework.test.web.servlet.assertj.MockMvcTester;
import org.springframework.web.context.WebApplicationContext;
import java.text.SimpleDateFormat;
import java.util.Date;
import static org.assertj.core.api.Assertions.assertThat;
@SpringJUnitWebConfig(MyWebConfig.class)
public class TradeControllerTest {
@Autowired
private WebApplicationContext webApplicationContext;
private MockMvcTester mockMvc;
@BeforeEach
public void setUp() {
mockMvc = MockMvcTester.from(webApplicationContext);
}
@Test
public void testValidDateRequest() throws Exception {
String tradeDate = "2020-02-29";
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
Date expectedDate = formatter.parse(tradeDate);
assertThat(mockMvc.get().uri("/trade")
.param("tradeDate", tradeDate))
.hasStatusOk()
.hasBodyTextEqualTo("request received for " + expectedDate);
}
@Test
public void testInvalidDateRequest() {
assertThat(mockMvc.get().uri("/trade")
.param("tradeDate", "12/25/2024"))
.hasStatus(HttpStatus.BAD_REQUEST);
}
}
mvn clean test -Dtest=TradeControllerTest Output$ mvn clean test -Dtest=TradeControllerTest [INFO] Scanning for projects... [INFO] [INFO] --------< com.logicbig.example:custom-web-binding-initializer >--------- [INFO] Building custom-web-binding-initializer 1.0-SNAPSHOT [INFO] from pom.xml [INFO] --------------------------------[ war ]--------------------------------- [INFO] [INFO] --- clean:3.2.0:clean (default-clean) @ custom-web-binding-initializer --- [INFO] Deleting D:\example-projects\spring-mvc\custom-web-binding-initializer\target [INFO] [INFO] --- resources:3.3.1:resources (default-resources) @ custom-web-binding-initializer --- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory D:\example-projects\spring-mvc\custom-web-binding-initializer\src\main\resources [INFO] [INFO] --- compiler:3.15.0:compile (default-compile) @ custom-web-binding-initializer --- [INFO] Recompiling the module because of changed source code. [INFO] Compiling 3 source files with javac [debug target 25] to target\classes [INFO] [INFO] --- resources:3.3.1:testResources (default-testResources) @ custom-web-binding-initializer --- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory D:\example-projects\spring-mvc\custom-web-binding-initializer\src\test\resources [INFO] [INFO] --- compiler:3.15.0:testCompile (default-testCompile) @ custom-web-binding-initializer --- [INFO] Recompiling the module because of changed dependency. [INFO] Compiling 2 source files with javac [debug target 25] to target\test-classes [INFO] [INFO] --- surefire:3.2.5:test (default-test) @ custom-web-binding-initializer --- [INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider [WARNING] file.encoding cannot be set as system property, use <argLine>-Dfile.encoding=...</argLine> instead [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running com.logicbig.example.TradeControllerTest [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.676 s -- in com.logicbig.example.TradeControllerTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 3.077 s [INFO] Finished at: 2026-05-12T09:03:21+08:00 [INFO] ------------------------------------------------------------------------ INFO: Completed initialization in 1 ms INFO: Completed initialization in 1 ms
Example ProjectDependencies and Technologies Used: - spring-webmvc 7.0.6 (Spring Web MVC)
Version Compatibility: 3.2.9.RELEASE - 7.0.6 Version compatibilities of spring-webmvc with this example: Versions in green have been tested.
- spring-test 7.0.6 (Spring TestContext Framework)
- jakarta.servlet-api 6.1.0 (Jakarta Servlet API documentation)
- junit-jupiter-engine 6.0.3 (Module "junit-jupiter-engine" of JUnit)
- hamcrest 3.0 (Core API and libraries of hamcrest matcher framework)
- assertj-core 3.26.3 (Rich and fluent assertions for testing in Java)
- JDK 25
- Maven 3.9.11
|