22.3 Implementing Controllers
控制器提供对通常通过服务接口定义的应用程序行为的访问。 控制器解释用户输入并将其转换为由视图表示给用户的模型。 Spring以非常抽象的方式实现控制器,使您能够创建各种各样的控制器。
Spring 2.5引入了一种基于注释的编程模型,用于使用诸如@RequestMapping,@RequestParam,@ModelAttribute等注释的MVC控制器。 此注解支持可用于Servlet MVC和Portlet MVC。 以这种风格实现的控制器不必扩展特定的基类或实现特定的接口。 此外,它们通常不直接依赖于Servlet或Portlet API,尽管您可以轻松配置对Servlet或Portlet设施的访问。
@Controller
public class HelloWorldController {
@RequestMapping("/helloWorld")
public String helloWorld(Model model) {
model.addAttribute("message", "Hello World!");
return "helloWorld";
}
}
您可以看到,@Controller和@RequestMapping注释允许灵活的方法名称和签名。 在这个特殊的例子中,该方法接受一个Model并返回一个视图名称作为一个String,但是可以使用各种其他的方法参数和返回值,如本节稍后所述。 @Controller和@RequestMapping和许多其他注释构成了Spring MVC实现的基础。 本节介绍这些注释以及它们在Servlet环境中最常用的注释。
22.3.1 Defining a controller with @Controller
@Controller注释表示特定的类用于控制器的角色。 Spring不需要扩展任何控制器基类或引用Servlet API。 但是,如果需要,您仍然可以参考Servlet特定的功能。
@Controller注释作为注释类的构造型,表示其作用。 调度程序扫描这些注释类的映射方法,并检测@RequestMapping注释(请参阅下一节)。
您可以使用调度程序上下文中的标准Spring bean定义来明确定义带注释的控制器bean。 但是,@Controller构造型还允许自动检测,与Spring通用支持对齐,用于检测类路径中的组件类并自动注册它们的bean定义。
要启用自动检测这些带注释的控制器,您可以向组态添加组件扫描。 使用spring-context模式,如以下XML代码片段所示:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="org.springframework.samples.petclinic.web"/>
<!-- ... -->
</beans>
22.3.2 Mapping Requests With @RequestMapping
您可以使用@RequestMapping注释将诸如/约会的URL映射到整个类或特定的处理程序方法。 通常,类级注释将特定的请求路径(或路径模式)映射到表单控制器上,其他方法级注释缩小了特定HTTP方法请求方法(“GET”,“POST”等)的主映射, 或HTTP请求参数条件。Petcare示例中的以下示例显示了使用此注释的Spring MVC应用程序中的控制器:
@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用在很多地方。 第一个用法是类型(类)级别,这表示此控制器中的所有处理程序方法都相对于/约会路径。 get()方法还有一个@RequestMapping细化:它只接受GET请求,这意味着/约会的HTTP GET调用此方法。 add()有一个类似的细化,getNewForm()将HTTP方法和路径的定义组合成一个,以便通过该方法处理约会/新的GET请求。