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)。