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("/*"); } }
|