Spring MVC
MVC框架
Spring MVC常用组件
Spring MVC项目
- 浏览器发送一个请求,若请求地址与 web.xml 中配置的前端控制器(DispatcherServlet)的 url-pattern 相匹配,则该请求就会被前端控制器 DispatcherServlet 拦截;
- 前端控制器(DispatcherServlet )会读取 SpringMVC 的核心配置文件,通过组件扫描获取到所有的控制器(Contorller);
- 将请求信息和控制器中所有控制器方法标识的 @RequestMapping 注解的 value、method 等属性值进行匹配。若匹配成功,则将请求交给对应的 @RequestMapping 注解所标识的控制器方法处理;
- 处理请求的方法会返回一个字符串类型的视图名称,该视图名称会被 Spring MVC 配置文件中配置的视图解析器(ViewResolver)解析真正的视图(View)对象,最终展示给客户端。
package net.biancheng.c.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloController {
@RequestMapping("/")
public String sayHello() {
//视图名,视图为:视图前缀+index+视图后缀,即 /WEB-INF/template/index.html
return "index";
}
@RequestMapping("/login")
public String welcome() {
//视图名,视图为:视图前缀+login+视图后缀,即 /WEB-INF/template/login.html
return "login";
}
@RequestMapping("/register")
public String success() {
//视图名,视图为:视图前缀+register+视图后缀,即 /WEB-INF/template/register.html
return "register";
}
}
SpringMVC执行流程
@RequestMapping注解
-
使用方式
- 修饰方法:要访问该方法,请求路径必须与这个value值相同
- 修饰类:要访问类中的任意方法,都要带上这个父路径
-
属性
-
value属性:用于设置请求地址
-
name属性:用于注释
-
method属性:method 属性的取值是一个 RequestMethod 类型的数组,表示一个控制器方法支持多种方式的请求,常用的请求方式有 GET、POST、DELETE、PUT 等。如果一个控制器方法没有设置 @RequestMapping 注解的 method 属性,则说明该控制器方法支持全部请求类型,可以处理所有类型的请求。
-
params属性:取值是一个字符串类型的数组,用于指定请求中的参数,只有当请求中携带了符合条件的参数时,控制器方法才会对该请求进行处理
@RequestMapping(value = "/testParam", params = {"name=z5onk0", "url=http://www.baidu.com"}) @ResponseBody public String testParam() { return "success"; } //以上代码表示,只有当请求中同时携带 name 和 url 两个请求参数,且参数值必须分别为 “z5onk0” 和“http://www.baidu.com”时,控制器方法 testParam() 才会对该请求进行处理 。
-
headers属性:取值是一个字符串类型的数组,用于设置请求中请求头信息,只有当请求中携带指定的请求头信息时,控制器方法才会处理该请求
-
Spring MVC获取请求参数
-
通过HttpServletRequest
@RequestMapping("/getRequestParam") public String requestParam(HttpServletRequest request) { String name = request.getParameter("name"); String url = request.getParameter("url"); System.out.println("name:" + name); System.out.println("url:" + url); return "index"; }
-
通过形参获取:在 Controller 的控制器方法中设置与请求参数同名的形参,以获取请求中携带的参数。需要注意这种方式是无视参数的数据类型的,我们可以在控制器方法中使用 String 字符串类型的形参接收所有的请求参数;并且不适用于参数过多的情况
@RequestMapping("/test") public String test(String name, String language) { System.out.println("a:" + a); System.out.println("b:" + b); return "success"; }
-
通过实体类对象获取(推荐):在 Controller 控制器方法的形参中设置一个实体类形参,如果请求参数的参数名与实体类中的属性名一致,那么 Spring MVC 会自动将请求参数封装到该实体类对象中
-
在net.biancheng.c.entity 包下,创建一个名为 User 的实体类
package net.biancheng.c.entity; public class User { private String UserId; private String UserName; private Integer age; public String getUserId() { return UserId; } public void setUserId(String userId) { UserId = userId; } public String getUserName() { return UserName; } public void setUserName(String userName) { UserName = userName; } @Override public String toString() { return "User{" + "UserId='" + UserId + '\'' + ", UserName='" + UserName + '\'' + ", age=" + age + '}'; } }
-
创建一个名为 UserController 的 Controller 类
package net.biancheng.c.controller; import net.biancheng.c.entity.User; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class UserController { /** * 通过实体类获取请求参数 * * @param user * @return */ @RequestMapping("/getUser") public String getUser(User user) { System.out.println("userId:" + user.getUserId()); System.out.println("userName:" + user.getUserName()); System.out.println("password:" + user.getPassword()); return "success"; } }
-
Spring MVC域对象共享
-
Controller 在接收到 Model 层返回的模型数据后,下一步就是将模型数据通过域对象共享的方式传递给 View 视图进行渲染,最终返回给客户端展示
-
域对象是服务器在内存上创建的一块存储空间,主要用不同动态资源之间的数据传递和数据共享。在 Spring MVC 中,常用的域对象有 request 域对象、session 域对象、application 域对象等
-
方式:
-
使用 Servlet API 向 request 域对象中共享数据
@RequestMapping("/testServletAPI") public String testServletAPI(HttpServletRequest request) { request.setAttribute("testScope", "hello,Servet API"); return "success"; }
-
使用 ModelAndView 向 request 域对象中共享数据
-
model 负责数据共享,而 view 则主要用于设置视图,实现页面的跳转
@RequestMapping("/testModelAndView") public ModelAndView testModelAndView() { /** * ModelAndView有Model和View的功能 * Model主要用于向请求域共享数据 * View主要用于设置视图,实现页面跳转 */ ModelAndView mav = new ModelAndView(); //向请求域共享数据 mav.addObject("testScope", "hello,ModelAndView"); //设置视图,实现页面跳转 mav.setViewName("success"); return mav; }
-
-
使用 Map 向 request 域对象中共享数据
@RequestMapping("/testMap") public String testMap(Map<String, Object> map) { map.put("testScope", "hello,Map"); return "success"; }
-
-
实例
-
定义user和product实体类;定义userDao和productDao类,将多个user和product放入字典和列表中,并暴露方法;定义userService和productService,对Dao暴露的方法进行封装
-
定义LoginController
@Controller public class LoginController { @Autowired private UserService userService; @RequestMapping("/user") public String sayHello() { return "user"; } @RequestMapping("/login") public String login(User user, HttpServletRequest request) { User user2 = userService.getUserByUserName(user.getUserName()); if (user2 != null && user2.getPassword().equals(user.getPassword())) { HttpSession session = request.getSession(); session.setAttribute("loginUser", user2); return "redirect:/getProductList"; } request.setAttribute("msg", "账号或密码错误!"); return "user"; } }
-
定义ProductController
@Controller public class ProductController { @Autowired private ProductService productService; @RequestMapping("/getProductList") public ModelAndView getProductList() { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName("productList"); List<Product> productList = productService.getProductList(); modelAndView.addObject(productList); return modelAndView; } @RequestMapping("/getProduct") public String getProduct(Integer productId, Model model) { Product productById = productService.getProductById(productId); model.addAttribute("product", productById); return "product"; } }
-
Spring MVC视图和视图解析器
- 无论控制器方法的返回值是哪种类型,Spring MVC 内部最终都会将它们封装成一个 ModelAndView 对象
- 仅仅是一个 String 类型的逻辑视图名(View Name)而已,例如“success”、“index”等。这种情况下,Spring MVC 就需要借助 ViewResolver(视图解析器)将 ModelAndView 对象中逻辑视图名解析为真正的 View 视图对象,然后才能响应给客户端展示
- 需要在视图解析器中配置视图前缀和视图后缀,用来转化控制器返回的字符串
Spring MVC请求转发与重定向
-
请求转发
-
当控制器方法中所设置的逻辑视图名称以“forward:”为前缀时,该逻辑视图名称不会被 Spring MVC 配置的视图解析器解析,而是会将前缀“forward:”去掉,以剩余部分作为最终路径通过转发的方式实现跳转
-
通过String类型的返回值实现转发
@RequestMapping("/testDispatcher") public String testDispatcher() { return "forward:/login"; } //即路径“/testDispatcher”请求最终会被转发到 “/login”上进行处理
-
通过 ModelAndView 实现转发
@RequestMapping("/testDispatcher") public ModelAndView testDispatcher() { ModelAndView modelAndView = new ModelAndView(); //设置逻辑视图名 modelAndView.setViewName("forward:/login"); return modelAndView; }
-
-
重定向
- 当控制器方法中所设置的视图名称以“redirect:”为前缀时,该视图名称不会被 Spring MVC 配置的视图解析器解析,而是会将前缀“redirect:”去掉,以剩余部分作为最终路径通过重定向的方式实现跳转
- 通过String类型的返回值实现转发
- 通过 ModelAndView 实现转发
- 重定向和转发的区别:转发不会改变导航栏里的url
Spring MVC实现RESTful
-
通过 @RequestMapping +@PathVariable 注解的方式,来实现 RESTful 风格的请求
-
通过@RequestMapping 注解的路径设置
@RequestMapping("/testRest/{id}/{username}", method = RequestMethod.DELETE)
-
通过 @PathVariable 注解绑定参数
@RequestMapping("/testRest/{id}/{username}") public String testRest(@PathVariable("id") String id, @PathVariable("username") String username) { System.out.println("id:" + id + ",username:" + username); return "success"; }
Spring MVC类型转化器
-
作用是在控制器方法对请求进行处理前,先获取到请求发送过来的参数,并将其转换为控制器方法指定的数据类型,然后再将转换后的参数值传递给控制器方法的形参,这样后台的控制器方法就可以正确地获取请求中携带的参数了
-
Spring MVC 对于基本类型(例如 int、long、float、double、boolean 以及 char 等)已经做好了基本类型转换
-
对于一些较为复杂类型的转换,例如 String 转换 Date 类型,以及开发人员自定义格式的数据的转换等,就需要我们根据自身的需求开发自定义类型转换器来转换
/** * 自定义日期转换器 */ public class MyDateConverter implements Converter<String, Date> { private String datePatten = "yyyy-MM-dd"; @Override public Date convert(String source) { System.out.println("前端页面传递过来的时间为:" + source); SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePatten); try { return simpleDateFormat.parse(source); } catch (ParseException e) { throw new IllegalArgumentException("无效的日期格式,请使用正确的日期格式" + datePatten); } } } //还需要将其注册到Spring MVC的核心配置文件中,才能生效
Spring MVC格式化器
-
Spring 提供了一个 Formatter
接口, T 表示目标数据类型,它被称为格式化转换器;特殊数据都要经过一定的格式化处理才能够正常使用 -
同Converter的区别:Formatter 的源类型必须是 String 类型,而 Converter 的源类型可以是任意数据类型
-
定义格式化器需要重写parse和print方法
-
日期格式化
-
DateTimeFormat 注解可对 java.util.Date、java.util.Calendar、java.long.Long 等时间类型的数据进行标注
-
控制器中:
@DateTimeFormat(pattern = "yyyy-MM-dd") private Date date;
-
模板中:
<tr> <td>日期:</td> <td th:text="${#dates.format(market.getDate(),'yyyy-MM-dd')}"></td> </tr>
-
-
数值格式化
-
@NumberFormat 注解可以用来格式化任何数字基本类型
-
注解为货币类型
@NumberFormat(style = NumberFormat.Style.CURRENCY) private BigDecimal money;
-
JSON数据交互
-
JSON转换注解
-
实例
-
controller
/** * 查看或回显商品信息,get:查看商品信息 update:为修改页回显的商品信息 */ @ResponseBody @RequestMapping("/product/{productId}") public Product getProduct(@PathVariable("productId") String productId) { Product product = productDao.getProductById(productId); return product; } /** * 新增商品 * * @param product * @return */ @ResponseBody @RequestMapping(value = "/product", method = RequestMethod.POST) public Product addProduct(@RequestBody Product product) { productDao.addProduct(product); return product; }
-
product_list.html
-
主页面添加表格用来展示商品信息
<tbody th:id="tb">
-
通过ajax返回商品信息
$(function () { $.ajax({ //请求路径 url: "http://localhost:8080/springmvc-json-demo/getProductList1", //请求类型 type: "get", //定义发送请求的数据格式为JSON字符串 contentType: "application/json;charset=utf-8", //定义回调响应的数据格式为JSON字符串,该属性可以省略 dataType: "json", //成功响应的结果 success: function (data) { if (data != null) { var html = ""; for (var i = 0; i < data.length; i++) { var product = data[i]; html += "<tr>" + "<td>" + product.productId + "</td>" + "<td>" + product.productName + "</td>" + "<td>" + product.price + "</td>" + "<td>" + product.stock + "</td>" + "<td><button onclick='lookPage(" + product.productId + ")'>查看商品</button>" + "<button onclick='editPage(" + product.productId + ")';>修改商品</button>" + "<button onclick='deletePro(" + product.productId + ")';>删除商品</button></td>" + "</tr>" } $("#tb").html(html); } } }); })
-
首先通过css将增删查改的div块设置为不可见
<style type="text/css"> #addWindow, #editWindow { display: none; position: absolute; top: 25%; left: 25%; width: 30%; height: 40%; padding: 20px; border: 3px solid #ccc; background-color: white; z-index: 2; overflow: auto; } </style>
-
每个商品信息栏后有增删查改,通过click方法,将addWindow/editWindow设置为可见
$(document).ready(function () { //点击新增商品,弹出新增商品弹窗 $("#btn").click(function () { $("#addWindow").slideDown(300); //slideDown方法以滑动方式显示被选元素 $("#backGround").show(); });
-
addWindow用来展示增加商品的页面
<div id="addWindow"> <label id="x" style="position: absolute;top:2px;left: 95%;font-size: 25px;">x</label> <h4>新增商品</h4> <form th:action="@{/product}" method="post"> <table id="table-box" style="margin: auto"> <tr> <td>商品 ID:</td> <td><input type="text" id="productId" name="productId" required></td> </tr> <tr> <td>商品名称:</td> <td><input type="text" id="productName" name="productName" required></td> </tr> <tr> <td colspan="2" align="center"><input id="addPro" type="submit" value="新增商品"> <input type="reset" id="reset" value="重置"> </td> </tr> </table> </form> </div> <div id="backGround"></div>
-
填完商品信息,点击提交后,通过jquery click调用add方法,向后端请求数据,返回的数据append到商品详情页
$("#addPro").click(function () { add(); $("#addWindow").slideUp(300); $("#backGround").hide(); $("#reset").trigger("click") }); $.ajax({ //请求路径 url: "http://localhost:8080/springmvc-json-demo/product", //请求类型 type: "post", data: JSON.stringify({ productId: productId, productName: productName, }), //定义发送请求的数据格式为JSON字符串 contentType: "application/json;charset=utf-8", //定义回调响应的数据格式为JSON字符串,该属性可以省略 dataType: "json", //成功响应的结果 success: function (data) { if (data != null) { var product = data; var html = "<tr>" + "<td>" + product.productId + "</td>" + "<td>" + product.productName + "</td>" + "<td><button onclick='lookPage(" + product.productId + ")'>查看商品</a>" + "</tr>"; $("#tb").append(html); } }
-
-