1、本文内容
2、控制器的声明
2.1、启用组件自动扫描
2.2、使用@Controller标注
3、映射请求路径
3.1、在类上使用 @RequestMapping声明
3.2、在方法上使用@RequestMapping声明
3.3、@GetMapping、@PostMapping、@PutMapping等
4、获取URL请求参数
4.1、利用请求路径获取参数 URI Template Pattern & @PathVariable
4.2、获取URL请求参数 @RequestParam
5、获取表单数据
6、控制器返回视图
6.1、返回视图
6.2、携带数据返回视图
7、控制器转跳
7.1、控制器之间跳转
7.2、跳转到外部链接
Controller是由很多内容组成的,包括将一个类配置为控制器、将类或方法映射为请求路径、从URL请求中解析参数、从表单中解析参数、控制器之间的跳转、请求的重定向、返回视图、构造模型等等内容
本文对这些控制器的常用部分做一个大致的梳理
本文的内容主要基于Java API 和注解方式配置
Spring配置中的启用组件自动扫描配合使用,将控制器所在的包设置为自动扫描的目标目录
package com.oolong.config; import com.oolong.controller.ControllerScanHook; @Configuration @EnableWebMvc @ComponentScan(basePackageClasses={ControllerScanHook.class}) public class WebConfig extends WebMvcConfigurerAdapter { }
package com.oolong.controller; import org.springframework.stereotype.Controller; @Controller public class HomeController { }
这相当于给这个控制器中的所有方法定义一个跟路径,然后在其方法上使用 @RequestMapping配置映射时,所有的映射都是相对于类声明上配置的这个跟路径的相对路径
@Controller @RequestMapping("/appointments") public class AppointmentsController { private final AppointmentBook appointmentBook; @Autowired public AppointmentsController(AppointmentBook appointmentBook) { this.appointmentBook = appointmentBook; } @RequestMapping(method = RequestMethod.GET) public Map<String, Appointment> get() { return appointmentBook.getAppointmentsForToday(); } @RequestMapping(path = "/{day}", method = RequestMethod.GET) public Map<String, Appointment> getForDay(@PathVariable @DateTimeFormat(iso = ISO.DATE) Date day, Model model) { return appointmentBook.getAppointmentsForDay(day); } @RequestMapping(path = "/new", method = RequestMethod.GET) public AppointmentForm getNewForm() { return new AppointmentForm(); } @RequestMapping(method = RequestMethod.POST) public String add(@Valid AppointmentForm appointment, BindingResult result) { if (result.hasErrors()) { return "appointments/new"; } appointmentBook.addAppointment(appointment); return "redirect:/appointments"; } }
观察这个案例中的内容,当使用 @RequestMapping标注在类上面声明了一个路径之后,方法的配置分为这样几种情况:
1)GET方式请求路径“/appointments”,将会进入 get() 方法
2)POST方式请求路径“/appointments”,将会进入 add() 方法
3)GET方式请求“/appointment/new”,将会进入 getNewForm() 方法
4)GET方式请求“/appointment/2016-12-06”,将会进入 getForDay() 方法【参考路径模版】
在上面的例子中@RequestMapping用在了两个地方,首相在类上声明,作为一个“基础”路径,然后在方法上的声明是相对于这个基础路径的相对路径。
除了这种用法,还可以不在类上面标注 @RequestMapping,而直接在方法上使用,这就表示绝对路径的声明,例如下面这样:
@Controller public class HomeController { @RequestMapping(value = "/", method = RequestMethod.GET) public String home() { return "home"; } @RequestMapping(value = "/welcome", method = RequestMethod.GET) public String welcome() { return "welcome"; } }
1)GET方式请求“/”,就会进入 home()方法
2)GET方式请求“/welcome”,就会进入 welcome() 方法
3.3、@GetMapping、@PostMapping、@PutMapping等
Spring框架从4.3开始引入了一个简化 @ReuqestMapping的注解,也就是:
@GetMapping、@PostMapping、@PutMapping、@DeleteMapping、@PatchMapping
这几个注解实际上就是设置了请求类型的 @RequestMapping 注解,与其对应关系可以参考下面的示例:
@RequestMapping(method = RequestMethod.GET) @GetMapping @RequestMapping(path = "/new", method = RequestMethod.GET) @GetMapping("/new") @RequestMapping(method = RequestMethod.POST) @PostMapping
4.1、利用请求路径获取参数 URI Template Pattern & @PathVariable
在使用 @RequestMapping或者@GetMapping等注解标注映射时,可以使用URL Template Patterns来设置请求路径,并从中获取参数。
例如,下面是一个URI的模版:
http://www.example.com/users/{userId}
注意这个模版,看起来与普通的URL没有什么区别,但是仔细看,其路径的最后一部分是 {userId} ,这样一个使用大括号括起来的字符串,这个就是一个URI模版,与这个模版对应的实际请求如下:
http://www.example.com/users/12345
在SpringMVC中,对于在 @RequestMapping中配置的这种请求路径,可以使用 @PathVariable注解来获取值:
@GetMapping("/owners/{ownerId}") public String findOwner(@PathVariable String ownerId, Model model) { Owner owner = ownerService.findOwner(ownerId); model.addAttribute("owner", owner); return "displayOwner"; }
在这个例子中的URI路径模版就是“/owners/{ownerId}”,然后在控制器方法的参数中使用 @PathVariable 注解标注一个参数,Spring MVC就会在获取请求i之后自动的将{ownerId}所表示的内容设置到这个参数中了。
注意,这里方法的参数名称要与URI中的{var}名称一样。
此外,@PathVariable注解可以指定方法参数对应的URI模版中占位符的名称:
@GetMapping("/owners/{ownerId}") public String findOwner(@PathVariable("ownerId") String theOwner, Model model) { // implementation omitted }
利用 @RequestParam可以从传统的URL中获取参数
@RequestMapping(value = "/requestparam", method = RequestMethod.GET)
public String acceptingRequestInput(@RequestParam("bookname") String name, @RequestParam("count") int count) {
System.out.println("bookname: " + name);
System.out.println("count: " + count);
return "requestInput";
}
注意这个方法的参数,这个方法有两个参数,分别使用 @RequestParam注解进行标注了,参考上一小节从路径中获取参数的方式,应该能够联想到这种方式是如何获取参数的。
对应于这个方法的请求路径:
/requestparam?bookname=thinkingjava&count=5
这种传统的URL请求,一个“?”后面跟着若干个键值对参数,可以使用 @RequestParam的方式获取参数。
获取表单数据可以直接在控制器中的方法中给出一个模型作为参数即可,表单中的name值对应着模型中的属性值,然后数据会被注入到一个自动构建的参数对象中。
form表单
<form action="${basePath}pm/usecase/save" method="POST"> <label for="num">Num</label> <input type="text" id="Num" name="num"><br> <label for="name">Name</label> <input type="text" id="name" name="name"><br> <label for="description">Description</label> <textarea rows="3" name="description" id="description"></textarea><br> <label for="status">Status</label> <select class="form-control" id="status" name="status"> <option value="1">创建</option> <option value="2">完成</option> </select><br> <label for="author">Author</label> <input type="text" id="author" name="author"><br> <label for="complitionDate">ComplitionDate</label> <input type="date" id="complitionDate" name="complitionDate"> <input type="time" id="complitionTime" name="complitionTime"><br> <button type="submit" class="btn btn-default">Save</button> </form>
模型
注意这个模型中的属性名称与表单中的name属性是一一对应的,如此才能够将值自动注入到相应的属性中
public class UsecaseViewModel { public UsecaseViewModel() { } private String wid; private String num; private String name; private String description; private int status; private String author; private String complitionDate; private String complitionTime; private String createDate; private String moduleWid; // getter and setter methods }
控制器方法
此处控制器的参数,直接使用这个模型类型,这告诉SpringMVC将相关的数据自动构造成这个类型的一个对象,然后注入到这个方法中
@RequestMapping(value = "/pm/usecase/save", method = RequestMethod.POST) public String saveUsecase(UsecaseViewModel model) { usecaseBus.saveUsecase(model); return "redirect:/pm/usecase/list/" + model.getModuleWid(); }
在SpringMVC中,控制器中的方法并不直接返回视图的完整路径,而是返回一个视图的名称,然后根据在Spring中配置的视图解析器,结合视图名称确定真正的视图。
根据在SpringMVC的配置类中配置的视图解析器:
@Bean public ViewResolver viewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/views/"); viewResolver.setSuffix(".jsp"); return viewResolver; }
上面配置的内容表示在控制器返回的视图名称加上前缀“/views/”,和后缀“.jsp”,然后到WebContent下寻找对应的文件。
因此对于下面的控制器中返回的视图名称:
@GetMapping("/welcome") public String welcome() { return "welcome"; }
其实际对应的视图文件应该是在WebContent目录下的:/views/welcome.jsp
上面的方式只是简单的返回一个视图,但在实际开发中通常需要将一些数据展示到视图中,那么如何将这些数据携带到视图中呢?
观察下面的方法,这个方法的返回值不再是一个字符串,而是一个 ModelAndView 类型,然后在这个方法中创建了一个ModelAndView 对象,这个构造函数中接受的字符串,就是视图的名称,此外可以向这个对象中添加键值对,这些添加到ModeAndView中的键值对实际上会被添加到Request中,然后被转发给JSP页面,这样我们就可以使用EL表达式直接获取这些值了。
@RequestMapping(value = "/todo/detail/{wid}") public ModelAndView todoDetail(@PathVariable String wid) { ModelAndView mv = new ModelAndView("todoDetail"); TodoDetailViewModel detail = todoBus.getDetail(wid); mv.addObject("detail", detail); return mv; }
在JSP页面,可以直接使用EL表达式获取,此外还可以使用JSTL。
<c:forEach items="${detail.items}" var="item" varStatus="status"> <tr> <td>${status.index+1 }</td> <td>${item.content}</td> <td>${item.createDate}</td> <td><a class="btn btn-default btn-sm" href="###" role="button">删除</a></td> </tr> </c:forEach>
使用“redirect:”前缀,后面跟上@RequestMapping中配置的路径,即可实现控制器之间的跳转
return "redirect:/pm/usecase/list/" + model.getModuleWid();
如果需要跳转到外部链接,则需要给出完整的路径,当然“redirect:”前缀也不能丢掉
return "redirect:http://myhost.com/absolutepath"