SpringMVC核心教程
发表于|更新于
|阅读量:
一、三层结构与MVC
1.三层结构与MVC关系架构图

二、SpringMVC的优势
灵活性: Spring MVC提供了灵活的配置选项,开发人员可以根据项目需求进行定制,而不受限于框架的约束。这种灵活性使得开发人员能够更好地控制应用程序的行为。
 
松耦合: Spring MVC采用了MVC架构模式,将应用程序分为模型、视图和控制器三个部分,使得它们之间的耦合度降低。这种松耦合性使得代码更易于维护、扩展和测试。
 
强大的验证支持: Spring MVC提供了强大的数据验证支持,可以轻松地对用户输入数据进行验证和处理,从而提高应用程序的安全性和稳定性。
 
集成性: Spring MVC可以与其他Spring框架模块(如Spring Core、Spring Security等)无缝集成,同时也可以与其他流行的框架和技术(如Hibernate、MyBatis、RESTful Web Services等)集成,为开发人员提供更多选择。
 
易于测试: Spring MVC框架支持单元测试和集成测试,开发人员可以轻松地编写和运行测试用例,确保应用程序的质量和稳定性。
 
丰富的功能: Spring MVC提供了许多功能,如请求映射、数据绑定、数据转换、国际化支持等,使得开发人员能够快速构建功能丰富的Web应用程序。
 
社区支持: Spring Framework拥有庞大的社区和活跃的开发团队,开发人员可以从社区中获取支持、解决问题,并分享经验和最佳实践。
 
三、SpringMVC处理流程
一图胜千言

四、常用注解
1. RequestParam
作用:把请求中指定名称的参数给控制器中的形参赋值。
属性:
	value:请求参数中的名称。
	required:请求参数中是否必须提供此参数。默认值:true。表示必须提供,如果不提供将报错。
1 2 3 4 5
   | @RequestMapping("/useRequestParam") public String useRequestParam(@RequestParam("name")Stringusername, @RequestParam(value="age",required=false)Integer age){   System.out.println(username+","+age);   return "success"; }
  | 
 
2.RequestBody
作用:
	用于获取请求体内容。直接使用得到是 key=value&key=value…结构的数据。
get请求方式不适用。
属性:
	required:是否必须有请求体。默认值是:true。当取值为 true 时,get 请求方式会报错。如果取值
为 false,get请求得到是 null。
1 2 3 4 5
   | @RequestMapping("/useRequestBody") public String useRequestBody(@RequestBody(required=false) String body){   System.out.println(body);   return "success"; }
  | 
 
3.PathVaribale
作用:
	用于绑定 url中的占位符。例如:请求 url中 /delete/**{id},这个{id}**就是 url占位符。
url支持占位符是 spring3.0之后加入的。是 springmvc支持 rest风格 URL的一个重要标志。
属性:
	value:用于指定 url中占位符名称。
	required:是否必须提供占位符。
1 2 3 4 5
   | @RequestMapping("/usePathVariable/{id}") public String usePathVariable(@PathVariable("id") Integer id){ 	System.out.println(id); 	return "success"; }
  | 
 
作用:
	用于获取请求消息头。
属性:
	value:提供消息头名称
	required:是否必须有此消息头
1 2 3 4 5 6
   | @RequestMapping("/useRequestHeader") public String useRequestHeader(@RequestHeader(value="Accept-Language", required=false)String requestHeader){ 	System.out.println(requestHeader); 	return "success"; }
  | 
 
5.CookieValue
作用:
	用于把指定 cookie名称的值传入控制器方法参数。
属性:
	value:指定 cookie的名称。
	required:是否必须有此 cookie。
1 2 3 4 5 6
   | @RequestMapping("/useCookieValue") public String useCookieValue(@CookieValue(value="JSESSIONID",required=false) String cookieValue){ 	System.out.println(cookieValue); 	return "success"; }
  | 
 
6.ModelAttribute
作用:
	通过使用@ModelAttribute注解,可以简化数据绑定、模型数据传递和表单数据处理过程,提高开发效率。
属性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
   | @ModelAttribute public User getDefaultUser() {   User user = new User();   user.setUsername("Guest");   user.setEmail("guest@example.com");   return user; }
  @GetMapping("/user") public String getUserProfile(@ModelAttribute("user") User user) {      return "userProfile"; }
  @PostMapping("/updateUser") public String updateUserProfile(@ModelAttribute("user") User user) {         return "userProfile"; }
   | 
 
7.SessionAttribute
作用:
	在Spring MVC中,@SessionAttributes注解用于将模型中的特定属性存储到会话(Session)中,以便它们在多个请求之间共享。这样可以在不同请求之间保持特定的模型属性,通常用于在多个请求之间传递数据或在整个会话期间保持数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
   | @Controller @SessionAttributes("user") public class UserController {
      @ModelAttribute("user")     public User getDefaultUser() {         User user = new User();         user.setUsername("Guest");         user.setEmail("guest@example.com");         return user;     }
      @GetMapping("/user")     public String getUserProfile() {         return "userProfile";     }
      @PostMapping("/updateUser")     public String updateUserProfile(@ModelAttribute("user") User user) {                  return "userProfile";     }
      @GetMapping("/otherPage")     public String getOtherPage(@ModelAttribute("user") User user) {                  return "otherPage";     } }
   | 
 
在上面的示例中:
@SessionAttributes("user")注解指定了需要存储在会话中的模型属性名为”user”。 
getDefaultUser()方法使用@ModelAttribute("user")注解,返回一个默认的User对象,并将其存储在模型中。由于”user”属性被标记为会话属性,因此它会自动存储到会话中。 
getUserProfile()方法用于显示用户信息页面,不接收任何参数。 
updateUserProfile()方法接收一个User对象作为参数,用于处理用户提交的表单数据。由于”user”属性被标记为会话属性,因此在这个方法中可以直接访问这个User对象。 
getOtherPage()方法演示了在其他请求中也可以访问存储在会话中的User对象。 
通过使用@SessionAttributes注解,可以在不同请求之间共享特定的模型属性,从而实现数据在会话期间的共享。
五、响应数据和结果视图
1.返回值类型
1.1 字符串
1 2 3 4 5
   | @RequestMapping("/testReturnString") public String testReturnString() { 	System.out.println("AccountController的 testReturnString 方法执行了。。。。"); 	return "success"; }
  | 
 
1.2 void
1 2 3 4 5 6 7 8 9 10 11 12 13
   | @RequestMapping("/testReturnVoid") public void testReturnVoid(HttpServletRequest request,HttpServletResponse response) throws Exception {        request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request, response);      response.sendRedirect("testRetrunString")      response.setCharacterEncoding("utf-8"); 	response.setContentType("application/json;charset=utf-8"); 	response.getWriter().write("json 串"); }
  | 
 
1.3 ModelAndView
1 2 3 4 5 6 7
   | @RequestMapping("/testReturnModelAndView") public ModelAndView testReturnModelAndView() { ModelAndView mv = new ModelAndView();   mv.addObject("username", "张三");   mv.setViewName("success");   return mv; }
  | 
 
2.转发和重定向
请求转发(Forward):
服务器内部跳转: 请求转发是服务器内部的跳转方式,所有操作都在服务器端完成,客户端浏览器不知道页面发生了跳转。
 
单次请求: 在请求转发过程中,浏览器发出一次请求,服务器接收到请求后将请求转发到另一个资源,最终响应给客户端的仍然是原始请求的结果。
 
共享Request域: 在请求转发过程中,原始请求和目标资源之间可以共享Request域中的数据,因为它们属于同一个请求。
 
地址栏不变: 由于请求转发是服务器内部跳转,因此浏览器的地址栏不会发生变化,仍然显示原始请求的URL。
 
重定向(Redirect):
客户端跳转: 重定向是一种客户端跳转方式,服务器端返回一个特殊的响应码(如302),告诉浏览器需要跳转到另一个URL。
 
两次请求: 在重定向过程中,浏览器会收到服务器返回的重定向响应,并发起一个新的请求到指定的URL,这样会导致两次请求-响应周期。
 
不共享Request域: 重定向会导致两次请求-响应周期,因此原始请求和重定向目标之间无法共享Request域中的数据。
 
地址栏变化: 由于重定向是客户端跳转,浏览器会显示新的URL地址,因此地址栏会发生变化。
 
选择使用场景:
2.1 forward转发
1 2 3 4 5 6 7 8
   | @RequestMapping("/testForward") public String testForward() { 	System.out.println("AccountController的 testForward 方法执行了。。。。");    	return "forward:/WEB-INF/pages/success.jsp";      return "forward:success"; }
  | 
 
需要注意的是,如果用了 formward:则路径必须写成实际视图 url,不能写逻辑视图。
它相当于“request.getRequestDispatcher(“url“).forward(request,response)”。
使用请求转发,既可以转发到 jsp,也可以转发到其他的控制器方法。
2.2 Redirec重定向
1 2 3 4 5
   | @RequestMapping("/testRedirect") public String testRedirect() { 	System.out.println("AccountController的 testRedirect 方法执行了。。。。"); 	return "redirect:success"; }
  | 
 
它相当于“response.sendRedirect(url)”。
需要注意的是,WEB-INF目录中的jsp页面,重定向无法找到;需要先写个返回WEB-INF目录中的jsp页面的控制器,重定向时再返回此控制器。
3.ResponseBody响应json数据
1 2 3 4 5 6 7 8 9 10
   | @Controller("jsonController") public class JsonController {      @RequestMapping("/testResponseJson")   public @ResponseBody Account testResponseJson(@RequestBody Account account) {     System.out.println("异步请求:"+account);     return account;   }    }
  | 
 
六、文件上传
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
   | package com.example.controller;
  import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.multipart.MultipartFile;
  import java.io.File; import java.io.IOException;
  @Controller public class FileUploadController {
      @RequestMapping(value = "/upload", method = RequestMethod.GET)     public String showUploadForm() {         return "upload";     }
      @RequestMapping(value = "/upload", method = RequestMethod.POST)     public String handleFileUpload(@RequestParam("file") MultipartFile file) {         if (!file.isEmpty()) {             try {                 String uploadsDir = "/uploads/";                 String realPathtoUploads = request.getServletContext().getRealPath(uploadsDir);                 if (!new File(realPathtoUploads).exists()) {                     new File(realPathtoUploads).mkdir();                 }
                  String orgName = file.getOriginalFilename();                 String filePath = realPathtoUploads + orgName;                 File dest = new File(filePath);                 file.transferTo(dest);                 return "uploadSuccess";             } catch (IOException e) {                 return "uploadFailure";             }         } else {             return "uploadFailure";         }     } }
   | 
 
在上面的示例中,我们创建了一个 FileUploadController 类来处理文件上传请求。showUploadForm 方法用于显示上传表单,handleFileUpload 方法用于处理文件上传请求。在 handleFileUpload 方法中,我们首先检查文件是否为空,然后将文件保存到服务器上的指定位置。
七、异常处理
1.异常处理流程图

2.自定义异常处理器
2.1 创建一个自定义异常类,例如CustomException:
1 2 3
   | public class CustomException extends RuntimeException {      }
  | 
 
2.2 创建一个实现HandlerExceptionResolver接口的异常处理器类,例如CustomExceptionHandler:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
   | import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
  public class CustomExceptionHandler implements HandlerExceptionResolver {
      @Override     public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {                  if (ex instanceof CustomException) {                          ModelAndView modelAndView = new ModelAndView("errorPage");             modelAndView.addObject("errorMessage", ex.getMessage());             return modelAndView;         }
                   return null;     } }
   | 
 
2.3 配置自定义异常处理器到Spring MVC配置文件中(例如dispatcher-servlet.xml):
1
   | <bean class="com.example.CustomExceptionHandler" />
   | 
 
2.4 创建一个错误页面(例如errorPage.jsp),用来显示异常信息:
1 2 3 4 5 6 7 8 9 10
   | <%@ page contentType="text/html;charset=UTF-8" %> <html> <head>     <title>Error Page</title> </head> <body>     <h1>Error</h1>     <p>${errorMessage}</p> </body> </html>
   | 
 
八、拦截器
实现HandlerInterceptor接口来自定义拦截器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
   | import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView;
  public class CustomInterceptor implements HandlerInterceptor {
      @Override     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {                           return true;     }
      @Override     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {              }
      @Override     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {              } }
   | 
 
将这个拦截器注册到Spring MVC配置中,可以通过Java配置或XML配置来实现
1 2 3 4 5 6 7 8 9 10 11 12 13
   | import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  @Configuration public class WebConfig implements WebMvcConfigurer {
      @Override     public void addInterceptors(InterceptorRegistry registry) {       	         registry.addInterceptor(new CustomInterceptor()).addPathPatterns("/*");     } }
   |