史上最简单的Spring Mvc入门教程(一)

1、Spring Mvc简介

Spring的模型-视图-控制器(MVC)框架是围绕一个DispatcherServlet来设计的,这个Servlet会把请求分发给各个处理器,并支持可配置的处理器映射、视图渲染、本地化、时区与主题渲染等,甚至还能支持文件上传。处理器是你的应用中注解了@Controller和@RequestMapping的类和方法,Spring为处理器方法提供了极其多样灵活的配置。Spring 3.0以后提供了@Controller注解机制、@PathVariable注解以及一些其他的特性,你可以使用它们来进行RESTful web站点和应用的开发。

2、Spring Mvc用DispatcherServlet处理请求

Spring MVC框架,与其他很多web的MVC框架一样:请求驱动;所有设计都围绕着一个中央Servlet来展开,它负责把所有请求分发到控制器;同时提供其他web应用开发所需要的功能。不过Spring的中央处理器,DispatcherServlet,能做的比这更多。它与Spring IoC容器做到了无缝集成,这意味着,Spring提供的任何特性,在Spring MVC中你都可以使用。

下图展示了Spring Web MVC的DispatcherServlet处理请求的工作流。熟悉设计模式的朋友会发现,DispatcherServlet应用的其实就是一个“前端控制器”的设计模式(其他很多优秀的web框架也都使用了这个设计模式)。

史上最简单的Spring Mvc入门教程(一)

DispatcherServlet其实就是个Servlet(它继承自HttpServlet基类),同样也需要在你web应用的web.xml配置文件下声明。你需要在web.xml文件中把你希望DispatcherServlet处理的请求映射到对应的URL上去。这就是标准的Java EE Servlet配置;下面的代码就展示了对DispatcherServlet和路径映射的声明:

代码块

<web-app>
<servlet>
<servlet-name>example/<servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet/<servlet-class>
<load-on-startup>1/<load-on-startup>
/<servlet>

<servlet-mapping>
<servlet-name>example/<servlet-name>
<url-pattern>/example/*/<url-pattern>
/<servlet-mapping>
/<web-app>

在上面的例子中,所有路径以/example开头的请求都会被名字为example的DispatcherServlet处理。

2.1 Spring Mvc处理流程

配置好DispatcherServlet以后,开始有请求会经过这个DispatcherServlet。此时,DispatcherServlet会依照以下的次序对请求进行处理:

  • 首先,搜索应用的上下文对象WebApplicationContext并把它作为一个属性(attribute)绑定到该请求上,以便控制器和其他组件能够使用它。属性的键名默认为DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE
  • 将地区(locale)解析器绑定到请求上,以便其他组件在处理请求(渲染视图、准备数据等)时可以获取区域相关的信息。如果你的应用不需要解析区域相关的信息,忽略它即可
  • 将主题(theme)解析器绑定到请求上,以便其他组件(比如视图等)能够了解要渲染哪个主题文件。同样,如果你不需要使用主题相关的特性,忽略它即可
  • 如果你配置了multipart文件处理器,那么框架将查找该文件是不是multipart(分为多个部分连续上传)的。若是,则将该请求包装成一个MultipartHttpServletRequest对象,以便处理链中的其他组件对它做进一步的处理。
  • 为该请求查找一个合适的处理器。如果可以找到对应的处理器,则与该处理器关联的整条执行链(前处理器、后处理器、控制器等)都会被执行,以完成相应模型的准备或视图的渲染
  • 如果处理器返回的是一个模型(model),那么框架将渲染相应的视图。若没有返回任何模型(可能是因为前后的处理器出于某些原因拦截了请求等,比如,安全问题),则框架不会渲染任何视图,此时认为对请求的处理可能已经由处理链完成了

【注】WebApplicationContext是实现ApplicationContext接口的子类,是专门为WEB应用准备的。

代码块

//获取spring容器
WebApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(request.getSession().getServletContext());
PermissionCheck pc = (PermissionCheck)applicationContext.getBean("permissionCheck");

3、Spring Mvc 控制器的实现

控制器作为应用程序逻辑的处理入口,它会负责去调用你已经实现的一些服务。通常,一个控制器会接收并解析用户的请求,然后把它转换成一个模型交给视图,由视图渲染出页面最终呈现给用户。Spring对控制器的定义非常宽松,这意味着你在实现控制器时非常自由。

Spring 2.5以后引入了基于注解的编程模型,你可以在你的控制器实现上添加@RequestMapping、@RequestParam、@ModelAttribute等注解。注解特性既支持基于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类型的视图名。

3.1、Spring Mvc 使用@controller注解定义一个控制器

@Controller注解表明了一个类是作为控制器的角色而存在的。Spring不要求你去继承任何控制器基类,也不要求你去实现Servlet的那套API。当然,如果你需要的话也可以去使用任何与Servlet相关的特性和设施。

@Controller注解可以认为是被标注类的原型(stereotype),表明了这个类所承担的角色。分派器(DispatcherServlet)会扫描所有注解了@Controller的类,检测其中通过@RequestMapping注解配置的方法

3.2、Spring Mvc 使用@RequestMapping注解映射请求路径

我们可以使用@RequestMapping注解来将请求URL,如/appointments等,映射到整个类上或某个特定的处理器方法上。一般来说,类级别的注解负责将一个特定(或符合某种模式)的请求路径映射到一个控制器上,同时通过方法级别的注解来细化映射,即根据特定的HTTP请求方法(“GET”“POST”方法等)、HTTP请求中是否携带特定参数等条件,将请求映射到匹配的方法上。

下面这段代码示例来自Petcare,它展示了在Spring MVC中如何在控制器上使用@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> get() {
return appointmentBook.getAppointmentsForToday();
}

@RequestMapping(path = "/{day}", method = RequestMethod.GET)
public Map<string> 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";
}
}
/<string>/<string>

在上面的示例中,许多地方都使用到了@RequestMapping注解。第一次使用点是作用于类级别的,它指示了所有/appointments开头的路径都会被映射到控制器下。get()方法上的@RequestMapping注解对请求路径进行了进一步细化:它仅接受GET方法的请求。这样,一个请求路径为/appointments、HTTP方法为GET的请求,将会最终进入到这个方法被处理。add()方法也做了类似的细化,而getNewForm()方法则同时注解了能够接受的请求的HTTP方法和路径。这种情况下,一个路径为appointments/new、HTTP方法为GET的请求将会被这个方法所处理。

getForDay()方法则展示了使用@RequestMapping注解的另一个技巧:URI模板

【注】没有指定请求必须是GET方法还是PUT/POST或其他方法,@RequestMapping注解就会默认映射所有的HTTP请求方法。如果仅想接收某种请求方法,请在注解中指定之@RequestMapping(method=GET)以缩小范围。

3.2.1、URI模板

URI模板是一个类似于URI的字符串,只不过其中包含了一个或多个的变量名。当你使用实际的值去填充这些变量名的时候,模板就退化成了一个URI。在URI模板的RFC提议中定义了一个URI是如何进行参数化的。比如说,一个这个URI模板http://www.example.com/users/{userId}就包含了一个变量名

userId。将值fred赋给这个变量名后,它就变成了一个URI:http://www.example.com/users/fred。

Spring MVC中你可以在方法参数上使用@PathVariable注解,将其与URI模板中的参数绑定起来:

代码块

@RequestMapping(path="/owners/{ownerId}", method=RequestMethod.GET)
public String findOwner(@PathVariable String ownerId, Model model) {
Owner owner = ownerService.findOwner(ownerId);
model.addAttribute("owner", owner);
return "displayOwner";
}

URI模板"/owners/{ownerId}"指定了一个变量,名为ownerId。当控制器处理这个请求的时候,ownerId的值就会被URI模板中对应部分的值所填充。比如说,如果请求的URI是/owners/fred,此时变量ownerId的值就是fred.

代码块

//为了处理@PathVariables注解,Spring MVC必须通过变量名来找到URI模板中相对应的变量。你可以在注解中直接声明:
@RequestMapping(path="/owners/{ownerId}}", method=RequestMethod.GET)
public String findOwner(@PathVariable("ownerId") String theOwner, Model model) {
// 具体的方法代码…
}

//或者,如果URI模板中的变量名与方法的参数名是相同的,则你可以不必再指定一次。只要你在编译的时候留下debug信息,Spring MVC就可以自动匹配URL模板中与方法参数名相同的变量名。

@RequestMapping(path="/owners/{ownerId}", method=RequestMethod.GET)
public String findOwner(@PathVariable String ownerId, Model model) {
// 具体的方法代码…
}

3.2.2、矩阵变量

矩阵变量可以在任何路径段落中出现,每对矩阵变量之间使用一个分号“;”隔开。比如这样的URI:"/cars;color=red;year=2012"。多个值可以用逗号隔开"color=red,green,blue",或者重复变量名多次"color=red;color=green;color=blue"。

如果一个URL有可能需要包含矩阵变量,那么在请求路径的映射配置上就需要使用URI模板来体现这一点。这样才能确保请求可以被正确地映射,而不管矩阵变量在URI中是否出现、出现的次序是怎样等。

下面是一个例子,展示了我们如何从矩阵变量中获取到变量“q”的值:

代码块

// GET /pets/42;q=11;r=22

@RequestMapping(path = "/pets/{petId}", method = RequestMethod.GET)
public void findPet(@PathVariable String petId, @MatrixVariable int q) {

// petId == 42
// q == 11

}

由于任意路径段落中都可以含有矩阵变量,在某些场景下,你需要用更精确的信息来指定一个矩阵变量的位置:

代码块

// GET /owners/42;q=11/pets/21;q=22

@RequestMapping(path = "/owners/{ownerId}/pets/{petId}", method = RequestMethod.GET)
public void findPet(
@MatrixVariable(name="q", pathVar="ownerId") int q1,
@MatrixVariable(name="q", pathVar="petId") int q2) {

// q1 == 11
// q2 == 22

}

你也可以声明一个矩阵变量不是必须出现的,并给它赋一个默认值:

代码块

// GET /pets/42

@RequestMapping(path = "/pets/{petId}", method = RequestMethod.GET)
public void findPet(@MatrixVariable(required=false, defaultValue="1") int q) {

// q == 1

}

3.3 Spring Mvc定义@RequestMapping注解的处理方法

3.3.1使用@RequestParam将请求参数绑定至方法参数

你可以使用@RequestParam注解将请求参数绑定到你控制器的方法参数上。

下面这段代码介绍了它的用法

代码块

@Controller
@RequestMapping("/pets")
@SessionAttributes("pet")
public class EditPetForm {
// ...
@RequestMapping(method = RequestMapping.GET)
public String setupForm(@RequestParam("petId") int petId, ModelMap model) {
Pet pet = this.clinic.loadPet(petId);
model.addAttribute("pet", pet);
return "petForm";
}

// ,..
}

若参数使用了该注解,则该参数默认是必须提供的,但你也可以把该参数标注为非必须的:只需要将@RequestParam注解的required属性设置为false即可(比如,@RequestParam(path="id", required=false))。

若所注解的方法参数类型不是String,则类型转换会自动地发生。

若@RequestParam注解的参数类型是Map<string>或者MultiValueMap<string>,则该Map中会自动填充所有的请求参数。/<string>/<string>

后续也会及时更新文章~,因为我也是初学者,所以难免会有错误;如果有误,希望大家在评论里指出文章的错误~


分享到:


相關文章: