Spring 4.1与Java 8 java.util.Optional
在Spring 4.1中,利用Java 8的 java.util.Optional
,通过 @RequestParam
、 @RequestHeader
和 @MatrixVariable
三个注解,支持了仅包含非空(non-null)的容器对象。有了Java 8的 java.util.Optional
,你可以保证你的参数永远不会为 null
。
Request Params (请求参数)
在这个例子中,我们将使用 @RequestParam
注解把 java.time.LocalData
绑定为 java.util.Optional
:
@RestController @RequestMapping("o") public class SampleController { @RequestMapping(value = "r", produces = "text/plain") public String requestParamAsOptional( @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) @RequestParam(value = "ld") Optional<LocalDate> localDate) { StringBuilder result = new StringBuilder("ld: "); localDate.ifPresent(value -> result.append(value.toString())); return result.toString(); } }
在Spring 4.1之前,可能会发生 no matching editors or coversion strategy was found
(找不到匹配的编辑或转换策略)异常,这在Spring 4.1中不再是一个问题。为了验证这个绑定能够有效的运行,我们编写了一个简单的集成测试:
@RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = Application.class) @WebAppConfiguration public class SampleSomeControllerTest { @Autowired private WebApplicationContext wac; private MockMvc mockMvc; @Before public void setUp() throws Exception { mockMvc = MockMvcBuilders.webAppContextSetup(wac).build(); } // ... }
在这第一个测试中,我们将检测是否这个绑定有效的运行并且返回有效的结果:
@Test public void bindsNonNullLocalDateAsRequestParam() throws Exception { mockMvc.perform(get("/o/r").param("ld", "2020-01-01")) .andExpect(content().string("ld: 2020-01-01")); }
在接下来的测试中,我们将不会传入 ld
参数:
@Test public void bindsNoLocalDateAsRequestParam() throws Exception { mockMvc.perform(get("/o/r")) .andExpect(content().string("ld: ")); }
两个测试都顺利通过了!
Request Header (请求头部)
类似的,我们可以把 @RequestHeader
绑定到 java.util.Optional
:
@RequestMapping(value = "h", produces = "text/plain") public String requestHeaderAsOptional( @RequestHeader(value = "Custom-Header") Optional<String> header) { StringBuilder result = new StringBuilder("Custom-Header: "); header.ifPresent(value -> result.append(value)); return result.toString(); }
然后测试:
@Test public void bindsNonNullCustomHeader() throws Exception { mockMvc.perform(get("/o/h").header("Custom-Header", "Value")) .andExpect(content().string("Custom-Header: Value")); } @Test public void noCustomHeaderGiven() throws Exception { mockMvc.perform(get("/o/h").header("Custom-Header", "")) .andExpect(content().string("Custom-Header: ")); }
Matrix Variables (数组变量)
在Spring 3.2中引入的 @MatrixVariable
注解表明了在一个路径段中的方法参数应该被绑定到一个名值对中:
@RequestMapping(value = "m/{id}", produces = "text/plain") public String execute(@PathVariable Integer id, @MatrixVariable Optional<Integer> p, @MatrixVariable Optional<Integer> q) { StringBuilder result = new StringBuilder(); result.append("p: "); p.ifPresent(value -> result.append(value)); result.append(", q: "); q.ifPresent(value -> result.append(value)); return result.toString(); }
以上的方法可以通过获取url /o/m/42;p=4;q=2
来调用。我们写个例子测试一下:
@Test public void bindsNonNullMatrixVariables() throws Exception { mockMvc.perform(get("/o/m/42;p=4;q=2")) .andExpect(content().string("p: 4, q: 2")); }
不幸的是,这个测试失败了。因为 在Spring MVC中, @MatrixVariable
注解默认是禁止的。为了使它能用,我们需要调整 RequestMappingHandlerMapping
的属性 removeSemicolonContent
,默认为true,设置为false。通过 WebMvcConfigurerAdapter
设置了这个属性,就像下面这样:
@Configuration public class WebMvcConfig extends WebMvcConfigurerAdapter { @Override public void configurePathMatch(PathMatchConfigurer configurer) { UrlPathHelper urlPathHelper = new UrlPathHelper(); urlPathHelper.setRemoveSemicolonContent(false); configurer.setUrlPathHelper(urlPathHelper); } }
现在,所有的测试都可以通过了。在 这里你可以找到这篇文章的示例源码(github)。