请求参数绑定
当用户发送请求时, 根据 Spring MVC 的请求处理流程, 前端控制器会请求处理器映射器返回一个处理器, 然后请求处理器适配器之心相应的处理器, 此时处理器映射器会调用 Spring Mvc 提供的参数绑定组件将请求的 key/value 数据绑定到 Controller 处理器方法对应的形参上. Spring MVC 使用 Converter 转换器可以进行各种类型的转换, 也可自定义 Converter 转换器, Spring MVC 默认转换器支持的类型有 HttpServletRequest,HttpServletResponse,HttpSession,Model,ModelMap. 其中 Model 是一个接口, ModelMap 是一个接口实现, 作用是将 model 数据填充到 request.
简单类型, 自定义类型
- //localhost:8080/springMvcNext/product/infoa?id=1
- @RequestMapping("infoa")
- public String productInfoa(Model model, Integer id) {
- model.addAttribute("message", "productid:" + id);
- return "product/info";
- }
备注: 如果 url 中参数名不是 id, 则不会绑定成功, 需要通过使用注解 RequestParam 绑定参数
自定义类型传递, 使用 pojo 传递 (Product)
- @RequestMapping(value="infob",method = RequestMethod.POST)
- public String productInfob(Model model, Product product) {
- model.addAttribute("message", "product-price:" + product.getPrice()+"product-name:" + product.getProductName());
- return "product/info";
- }
使用注解绑定参数
通过 RequestParam 注解绑定参数形参名与入参不一致的参数, RerquestParam 有三个参数属性, value 参数名, 指定要绑定的入参名, required 是否必须, 默认为 false,defaultValue 属性, 用于没有传递时赋默认值.
- //http://localhost:8080/springMvcNext/product/info?productId=1&name=fgsg
- @RequestMapping("info")
- public String productInfo(Model model, @RequestParam(name = "name", defaultValue = "test") String productName,
- @RequestParam(required = true) Integer productId) {
- model.addAttribute("message", "name:" + productName + "productid:" + productId);
- return "product/info";
- }
通过 RequestHeader 注解获取请求头的信息, RequestHeader 同样有三个参数属性 value,required,defaultvalue
- // http://localhost:8080/springMvcNext/product/info2
- // 输出产品信息: browser:Mozilla/5.0 (Windows NT 10.0; WOW64) ApplewebKit/537.36 (Khtml, like Gecko) Chrome/66.0.3359.181 Safari/537.36 language:zh-CN,zh;q=0.9
- @RequestMapping("info2")
- public String productInfo2(Model model, @RequestHeader("User-Agent") String browser,
- @RequestHeader(value = "Accept-Language", required = false, defaultValue = "null") String language) {
- model.addAttribute("message", "browser:" + browser + "language:" + language);
- return "product/info";
- }
通过 CookieValue 注解获取请求头的信息
- //http://localhost:8080/springMvcNext/product/info3
- // 输出: 产品信息: JSESSIONID:0FD3AFA5E445DADACBC1F07568970FEC
- @RequestMapping("info3")
- public String productInfo3(Model model, @CookieValue("JSESSIONID") String cookie) {
- model.addAttribute("message", "JSESSIONID:" + cookie);
- return "product/info";
- }
通过 HttpServletRequest 获取参数
使用 HttpServletRequest 获取请求参数, 当客户端通过 HTTP 协议访问服务器时, HTTP 请求头中的所有信息都封装在这个对象中, 开发人员通过这个对象的方法, 可以获得客户这些信息, HttpServletRequest 可以用于参数解析, Cookie 读取, http 请求字段, 文件上传
- @RequestMapping("info4")
- public String productInfo(String houseUnitInfo, HttpServletRequest request, HttpServletResponse response)
- throws IOException {
- String requestStr = charReader(request);
- System.out.println(requestStr);
- return "product/info";
- }
- private String charReader(HttpServletRequest request) throws IOException {
- BufferedReader br = request.getReader();
- String str, wholeStr = "";
- while ((str = br.readLine()) != null) {
- wholeStr += str;
- }
- // System.out.println(wholeStr);
- return wholeStr;
- }
测试:
结果:
Action 返回值
返回 ModelAndView
返回 ModelAndView 可以指定视图名和 model 数据, ModelAndView 提供的 addObject 方法来给这个模型添加数据, 添加的是一个键值对的数据
- @RequestMapping("info5")
- public ModelAndView productInfo5() {
- ModelAndView modelAndView = new ModelAndView();
- modelAndView.setViewName("product/detail");
- modelAndView.addObject("message", "return modelandview");
- //modelAndView.addObject("xxx", "yyy");
- return modelAndView;
- }
返回 void,Map,Model
返回 void,Map,Model 时, 返回对应的逻辑视图名称就是请求 url, 仍然遵循: prefix 前缀 + 视图名称 +suffix 后缀组成
- //1. 返回 Map// 访问视图: /springMvcNext/WEB-INF/view/product/detail.jsp
- @RequestMapping("detail")
- public Map<String, Object> detail2313() {
- Map<String, Object> map = new HashMap<String, Object>();
- map.put("message", "product detail");
- return map;
- }
- //2. 返回 void
- // 返回 void 时, 则响应的视图页面对应为访问地址
- // 访问视图: /springMvcNext/WEB-INF/view/product/info6.jsp
- @RequestMapping("info6")
- public void productInfo6() {
- }
但输出流中存在输出内容时, 则不会去查找视图, 而是将输入流中的内容直接响应到客户端, 响应的内容类型是纯文本
- @RequestMapping("info7")
- public void productInfo7(HttpServletResponse response) throws IOException {
- response.getWriter().write("<h2>void method</h2>");// 直接相应结果
- }
- @RequestMapping("info8")
- public void productInfo8(HttpServletResponse response) throws IOException {
- response.sendRedirect("detail"); // 重定向 访问: http://localhost:8080/springMvcNext/product/detail
- }
- //3. 返回 Model model 对象会用于页面渲染, 视图路径使用方法名, 与 void 类似. 示例代码如下:
- @RequestMapping("info9")
- public Model productInfo9(Model model) {
- model.addAttribute("message", "product detail");
- return model;
- }
返回 String(视图名)
返回视图名: Controller 类方法返回字符串可以指定逻辑视图名, 通过视图解析器解析为物理视图地址.
- @RequestMapping("info10")
- public String productInfo10(Model model)
- {
- model.addAttribute("message", "productInfo10");
- return "product/detail";
- }
Spring MVC 转发与重定向
使用 <mvc:view-controller > 标签转发
Spring MVC 中对与 WEB-INF 目录下面的 JSP 页面, 不能直接通过 URL 访问. 需要通过转发的方式, 而我们一般都是在控制器中做转发映射, 对应一些我们不需要其他操作的 JSP 页面, 我们可以使用 < mvc:view-controller path=""/> 来配置, 这样就可以不用再控制器中再去做转发映射.
- <!-- 配置直接进行转发的页面, 无须进入 handler 方法 -->
- <mvc:view-controller path="home" />
- <mvc:view-controller path="order/info" />
访问: http://localhost:8080/springMvcNext/order/info 和 http://localhost:8080/springMvcNext/home 不经过处理器
使用 forward 或者 redirect 进行视图转发与重定
重定向: Spring mvc 中可以在返回的结果前加上一个前缀 "redirect:", 可以重定向到一个指定的页面也可以是另一个 action
转发: Springmvc 中返回结果前加 "foword" 前缀, 注意: 转发是一次请求 (相同的 request), 地址栏的 URL 不会改变
- // 重定向
- // 访问: http://localhost:8080/springMvcNext/product/redirecttest 时 Url 将跳转 http://localhost:8080/springMvcNext/product/info10?redirectparas=test+redirect
- @RequestMapping("redirecttest")
- public String redirecttest(Model model) {
- model.addAttribute("redirectparas", "test redirect"); // 带参数跳转
- return "redirect:/product/info10";
- }
- // 转发
- // 访问 http://localhost:8080/springMvcNext/product/forwardtest url 不会跳转
- @RequestMapping("forwardtest")
- public String forwardtest(Model model){
- model.addAttribute("forwardparas", "test forward"); // 带参数跳转
- return "forward:/product/info10";
- }
异常处理
Spring MVC 中通过使用 @controlleradvice + @ ExceptionHandler 两个注解可以实现全局的异常捕捉.
@ExceptionHandler 注解的作用是当出现其定义的异常时进行处理的方法, 其可以使用 springmvc 提供的数据绑定, 比如注入 HttpServletRequest 等, 还可以接受一个当前抛出的 Throwable 对象
@ControllerAdvice 注解可以把异常处理器应用到所有控制器 @Controller , 而不是 @Controller 注解的单个控制器, 该异常处理器对当前控制器的所有方法有效; 如果单独某个控制器需要自定义处理异常, 不用顶层的异常处理器, 可以在当前控制器内用 @ExceptionHandler 注解 , 这样当前控制器的异常处理就在当前类中.
备注: 使用 ControllerAdvice 注解类里面的异常的处理的优先级低于直接定义在处理方法的类中
实现一个异常处理器:
- @ControllerAdvice
- public class ExceptionHandlers {
- @ExceptionHandler({ArithmeticException.class})
- public ModelAndView toException(Exception e){
- ModelAndView mv = new ModelAndView("home");
- System.out.println("gobal handler exception");
- // 虽然不能使用 Map 往 request 中存值, 但是可以使用下面的方法
- mv.addObject("error", e);
- System.out.println(e);
- return mv;
- }
- }
控制器
- @Controller
- @RequestMapping("exception")
- public class ExceptionController {
- // 示例 1
- @RequestMapping("test")
- public ModelAndView test() {
- System.out.println(10/0); // 抛异常
- return new ModelAndView("order/info", "message", "test exception");
- }
- }
拦截器
Spring MVC 提供了 Interceptor 拦截机制, 用于请求的预处理和后处理. 在 Spring MVC 中定义一个拦截器有两种方法: 第一种是实现 HandlerInterceptor 接口, 或者继承实现了 HandlerInterceptor 接口的类例如 (HandlerInterceptorAdapter); 第二种方法是实现 Spring 的 WebRequestInterceptor 接口 (该接口是针对请求的拦截器接口, 接口方法参数中没有 response), 或者继承实现了 WebrequestInterceptor 的类. 两种方式都是在 Handlerde 执行周期内进行拦截操作.
如果要实现 HandlerInterceptor 接口, 需要实现三个方法, preHandle,postHandle,afterCompletion
preHandle 方法在执行 Handler 方法之前执行, 返回 false 表示拦截请求, 不在执行后续逻辑, 可以用来做权限, 日志等.
postHandle 方法在执行 Handler 方法之后, 返回 modelAndView 之前执行, 由于该方法会在 DispatcherServlet 进行返回视图渲染之前被调用, 所以此方法多被用于同一处理返回视图, 例如将公用的模型数据添加到视图, 或者根据其他情况制定公用的视图.
afterCompletion 方法在执行完 Handler 之后执行, 由于是在 Controller 方法执行完毕后执行该方法, 所以该方法适合进行统一的异常或者日志处理操作.
实现 HandlerInterceptor 接口之后需要在 Spring 的类加载配置文件中配置拦截器实现类, 才能使拦截器起到拦截的效果. HandlerInterceptor 类加载配置有两种方式, 分别是 "针对 HandlerMapping 配置" 和 全局配置.
针对 HandlerMapping 配置需要在某个处理器映射器配置中将拦截器作为参数配置进去, 之后通过此处理器映射器的 handler 就会使用配置好的拦截器, 配置如下:
- <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
- <property name="interceptors">
- <list>
- <ref bean="hInterceptor1" />
- <ref bean="hInterceptor2" />
- </list>
- </property>
- <property name="order" value="1"></property>
- </bean>
- <bean id="hInterceptor1" class="com.sl.interceptors.TestInterceptor"></bean>
- <bean id="hInterceptor2" class="com.sl.interceptors.TestOrderInterceptor"></bean>
全局配置
- <!-- 配置自定义的拦截器 -->
- <mvc:interceptors>
- <bean class="com.sl.interceptors.TestInterceptor"></bean>
- </mvc:interceptors>
实现一个拦截器:
- @Component
- public class TestInterceptor implements HandlerInterceptor {
- /**
- * 当目标方法执行之前, 执行此方法, 返回 false, 则不再执行后续逻辑 postHandle,afterCompletion
- */
- public boolean preHandle(HttpServletRequest request,
- HttpServletResponse response, Object handler) throws Exception {
- System.out.println("First preHandle 最先执行..");
- return true;
- }
- /**
- * 执行目标方法之后, 渲染视图之前调. 在转向 jsp 页面之前, 可以对请求域中的属性, 或者视图进行修改
- */
- public void postHandle(HttpServletRequest request,
- HttpServletResponse response, Object handler,
- ModelAndView modelAndView) throws Exception {
- System.out.println("First postHandle 执行目标方法之后, 渲染视图之前调. 在转向 jsp 页面之前,");
- }
- /**
- * 在渲染视图之后被调用, 可以进行日志处理
- */
- public void afterCompletion(HttpServletRequest request,
- HttpServletResponse response, Object handler, Exception ex)
- throws Exception {
- System.out.println("First afterCompletion 渲染视图之后调用");
- }
- }
运行 controller 则可以看到拦截器执行记录.
如果定义多个拦截器, 则执行顺序如下:
1. preHandle 是按配置文件中的顺序执行的
2. postHandle 是按配置文件中的倒序执行的
3. afterCompletion 是按配置文件中的倒序执行的
测试验证:
拦截器的指定范围: 配置拦截器时可以根据需要制定拦截器作用范围, 针对特定处理器或方法进行拦截.
- <!-- 配置拦截器 -->
- <!-- 使用 bean 定义一个 Interceptor, 直接定义在 mvc:interceptors 根节点下则拦截所有的请求 -->
- <!-- 定义在 mvc:interceptor 下面的表示是对特定的请求才进行拦截的 -->
- <mvc:interceptors>
- <mvc:interceptor>
- <!-- 指定拦截器作用路径 -->
- <mvc:mapping path="/product/*" />
- <bean class="com.sl.interceptors.TestInterceptor"></bean>
- </mvc:interceptor>
- <mvc:interceptor>
- <mvc:mapping path="/order/*" />
- <bean class="com.sl.interceptors.TestOrderInterceptor"></bean>
- </mvc:interceptor>
- </mvc:interceptors>
- <mvc:exclude-mapping path=""/> 表示针对该 Path 不拦截 ,<mvc:mapping path=""/> 表示针对该 Path 拦截, Path 可以使用通配符.
来源: https://www.cnblogs.com/ashleyboy/p/9164724.html