SpringMVC中

搭建SpringMVC环境(一)创建项目、依赖、配置

步骤

  1. 创建项目、依赖
  2. 配置web.xml
  3. 配置springMVC.xml
  4. 编写index.html
  5. 测试

实现

1、创建项目、依赖

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>xxx.xxx</groupId>
    <artifactId>springmvc02</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.8.RELEASE</version>
        </dependency>
    </dependencies>
</project>

2、配置web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	version="2.5">

	<!--
		配置SpringMVC的前端控制器:DispatcherServlet
		1.关于拦截请求路径:
		   方式1: *.do  表示拦截所有以.do作为后缀的请求
		   			    缺点:路径不够简洁;不支持restful请求
		   方式2: /     表示拦截所有请求,不包括jsp请求
		   				注意:不能写/*
		 2. 如果拦截请求路径是/ ,会导致静态资源访问不了这个问题
	-->
	<servlet>
		<servlet-name>dispatcherServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:springmvc.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>dispatcherServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
</web-app>

3、配置springMVC.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--1. 注解扫描-->
    <context:component-scan base-package="xxx.xxx"/>

    <!--2. 配置视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--跳转路径的前缀-->
        <property name="prefix" value="/pages/"/>
        <!--跳转路径的后缀-->
        <property name="suffix" value=".jsp"/>
    </bean>

    <!--3. 注解驱动-->
    <mvc:annotation-driven/>
</beans>

4、编写index.html
在这里插入图片描述

5、测试
在这里插入图片描述

搭建SpringMVC环境(二)解决静态资源访问不了问题,方案1

问题

springMVC中拦截请求路径是“/”, 导致静态资源index.html访问不了。

分析

为什么导致这种情况的发生呢?

1、查看tomcat/conf/web.xml
在这里插入图片描述

2、自己项目配置的DispatcherServlet拦截的路径也是/, 会覆盖DefaultServlet。

3、DefaultServlet的作用: tomcat提供的默认sevlet,专门用于处理静态资源。

解决

让指定的静态资源找default名称对应的servlet即可:
在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	version="2.5">

	<!--
		配置SpringMVC的前端控制器:DispatcherServlet
		1.关于拦截请求路径:
		   方式1: *.do  表示拦截所有以.do作为后缀的请求
		   			    缺点:路径不够简洁;不支持restful请求
		   方式2: /     表示拦截所有请求,不包括jsp请求
		   				注意:不能写/*
		 2. 如果拦截请求路径是/ ,会导致静态资源访问不了这个问题
	-->
	<servlet>
		<servlet-name>dispatcherServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:springmvc.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>dispatcherServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>

	<!--
		如果自己项目的servlet拦截的请求路径是/,会覆盖tomcat中的DefaultServlet,
		而这个servlet是用来处理静态资源的。
		所以,DispatcherSevlet拦截请求路径是/,会导致静态资源访问不了。
		解决: 让静态资源去找DefaultServlet处理即可
	-->
	<servlet-mapping>
		<servlet-name>default</servlet-name>
		<url-pattern>*.html</url-pattern>
		<url-pattern>*.css</url-pattern>
		<url-pattern>*.js</url-pattern>
	</servlet-mapping>
</web-app>

搭建SpringMVC环境(二)解决静态资源访问不了问题,方案2

解决1:不推荐

<!--方式1:mapping 映射路径,就是访问路径。location 访问路径对应的真实路径。-->
<mvc:resources mapping="/pages/**" location="/pages/"/>
<mvc:resources mapping="/index.html" location="/index.html"/>

解决2:推荐 <mvc:default-servlet-handler/>

springmvc.xm添加如下配置:

    <!--4. 把静态资源交给默认sevlet处理(DefaultServlet), 解决拦截请求路径是/静态资源无法访问的问题。-->
    <mvc:default-servlet-handler/>

default-servlet-handler 将在 SpringMVC 上下文中定义一个DefaultServletHttpRequestHandler,它会对进入 DispatcherServlet 的请求进行筛查,如果发现是没有经过映射的请求,就将该请求交由 WEB 应用服务器默认的Servlet 处理。如果不是静态资源的请求,才由 DispatcherServlet 继续处理。

一般 WEB 应用服务器默认的 Servlet 的名称都是 default。若所使用的 WEB 服务器的默认 Servlet 名称不是 default,则需要通过 default-servlet-name 属性显式指定。

控制器方法返回值

说明

SpringMVC控制器方法参数,有哪些写法?

  1. 简单类型作为方法参数
  2. 对象作为方法参数
  3. servletApi作为方法参数

SpringMVC控制器方法返回值?

  1. 返回String字符串
  2. 返回void
  3. 返回ModelAndView对象
  4. 返回任意对象。(配置@ResponseBody注解)
  5. Map
  6. Model
  7. ModelMap

示例

第一步:编写控制器

@Controller
public class ReturnController {
    /**
     * 控制器方法的返回值(1)返回字符串 A 普通字符串
     */
    @RequestMapping("returnString")
    public String returnString(){
        return "success"; // 转发
    }

    /**
     * 控制器方法的返回值(1)返回字符串 B forward关键字,实现转发
     * 应用场景:
     *      转发后的资源与视图解析器配置的前缀后缀不一致时候使用。
     */
    @RequestMapping("forward")
    public String forward(){
        // 需求:转发到页面  http://localhost:8080/index.html
        return "forward:/index.html";
    }

    /**
     * 控制器方法的返回值(1)返回字符串 C redirect关键字,实现重定向
     * 应用场景:
     *      转发后的资源与视图解析器配置的前缀后缀不一致时候使用。
     */
    @RequestMapping("redirect")
    public String redirect(){
        // 需求:重定向到页面  http://localhost:8080/index.html
        return "redirect:/index.html";
    }

    /**
     * 控制器方法的返回值(2)返回void
     */
    @RequestMapping("returnVoid")
    public void returnVoid(HttpServletResponse response) throws IOException {
        // 使用servletapi实现重定向
        response.sendRedirect("/index.html");
    }

    /**
     * 控制器方法的返回值(3)返回返回ModelAndView
     * ModelAndView
     *  1. Model:可以往request域中存储数据
     *  2. View:也可以设置转发的路径名称
     */
    @RequestMapping("mv")
    public ModelAndView mv() {
        ModelAndView mv = new ModelAndView();
        // 往模型中存储数据(自动存储到request域中)
        mv.addObject("cn","China");
        // 设置转发的路径名称 (会匹配视图解析器配置的前缀和后缀)
        mv.setViewName("success");
        return mv;
    }
}

@Controller
@RequestMapping("/order")
public class OrderController {

    //A. 控制器方法中传入servletApi,保存数据
    @RequestMapping("/save1")
    public String save1(HttpServletRequest request){
        request.setAttribute("cn","China");
        return "success";
    }

    //B. 通过控制器方法的返回值ModelAndView
    @RequestMapping("/save2")
    public ModelAndView save2(){
        // 传入参数:转发的页面名称(前缀后缀在视图解析器中配置)
        ModelAndView mv = new ModelAndView("success");
        mv.addObject("cn","China2");
        return mv;
    }

    //C. 控制器方法中通过Map参数存储数据到request域
    @RequestMapping("/save3")
    public String save3(Map<String,Object> map){
        // 存储数据到request域
        map.put("cn","China3");
        return "success";
    }

    //D. 控制器方法中通过Model、ModelMap参数,存储数据到request域
    @RequestMapping("/save4")
    public String save4(Model model){
        // 存储数据到request域
        model.addAttribute("cn","China4");
        return "success";
    }
    //E
    @RequestMapping("/save5")
    public String save5(ModelMap model){
        // 存储数据到request域
        model.addAttribute("cn","China5");
        return "success";
    }
}

第二步:页面

success.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>success</title>
</head>
<body>
    success!!! <br>
    <h3>从request请求域中获取数据(`page`<`request`<`session`<`application`):</h3>
    <%--requestScope 是一个关键字,只会从request请求域中找数据--%>
    ${requestScope.cn}
</body>
</html>

访问测试:
在这里插入图片描述

交互JSON数据:@RequestBody与@ResponseBody注解

介绍

@RequestBody

作用:在处理器方法形参上使用,把请求的json格式数据,转换成java对象。

@ResponseBody

作用:在处理器方法返回值上使用,或者方法上使用。把响应的java对象,转换成json格式数据。

注意

  1. @RequestBody、@ResponseBody实现对象与json之间的互换
    1. @RequestBody 把请求的json字符串,自动转换为对象
    2. @ResponseBody 把方法返回的对象,自动转json
  2. 如果要用这两个注解,必须要先引入jackson支持包

示例

步骤:

  1. 添加jackson支持包
  2. 引入jQuery类库
  3. 编写ajaxRequest.html,发送ajax异步请求
  4. 编写JsonController,使用@RequestBody、@ResponseBody
  5. 测试

演示:

  1. 添加jackson支持包

    <!--添加jackson支持包-->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.9.0</version>
    </dependency>
    
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.0</version>
    </dependency>
    
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>2.9.0</version>
    </dependency>
    
  2. 引入jQuery类库
    在这里插入图片描述

  3. 编写ajaxRequest.html,发送ajax异步请求

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>ajax</title>
        <script src="/js/jquery-3.3.1.min.js" type="text/javascript"></script>
        <script>
            $(function(){
                // 给按钮绑定点击事件
                $("#ajaxPost").click(function(){
                    // 发送异步请求,传入json字符串
                    $.ajax({
                        url : "/requestJson",
                        type: "post",
                        dataType : "json",
                        // 请求数据:注意这里一定要指定json字符串,要带上引号
                        data : '{"id":100,"name":"小明","money":100}',
                        // 这里的请求类型也要指定
                        contentType:"application/json;charset=UTF-8",
                        success:function(result){
                            console.log(result);
                            alert(result.id + "," + result.name + "," + result.money);
                        }
                    })
                })
            })
        </script>
    </head>
    <body>
    <h2>RequestBody获取请求JSON格式数据 & ResponseBody自动把对象转json并响应</h2>
    <button id="ajaxPost">测试ajax请求json与响应json</button>
    </body>
    </html>
    
  4. 编写JsonController,使用@RequestBody、@ResponseBody

    步骤1: 编写Account对象

    public class Account {
        private Integer id;
        private String name;
        private Double money;
    	// 省略get、set
    }
    

    步骤2: 编写控制器

    
    /**
     * 演示通过@RequestBody、@ResponsBody实现对象与json的转换
     */
    @Controller
    public class JsonController {
    
        /**
         * 请求地址:/requestJson
         * 请求的json字符串:'{"id":100,"name":"小明","money":100}'
         *
         * @RequestBody  把请求的json字符串,自动转换为java对象。json的key与对象属性要一致。
         * @ResponseBody 把方法返回的对象,自动转换为json
         * 注意:
         *      要先引入jackson支持包。
         */
        @ResponseBody
        @RequestMapping("/requestJson")
        public Account requestJson(@RequestBody Account account){
            // 返回的对象
            account.setName("xiaohong");
            account.setMoney(1d);
            return account;
        }
    }
    
  5. 测试

小贴士


1@PostMapping = @RequestMapping + RequestMethod.POST

    @ResponseBody
    //@RequestMapping(value = "/requestJson",method = RequestMethod.POST)
    @PostMapping("/requestJson")  // 同上
    public Account requestJson(@RequestBody Account account){
        // 返回的对象
        account.setName("qiuqiu");
        account.setMoney(1d);
        return account;
    }
   
2@RestController
    
//@Controller
@RestController  //相当于:@Controller + @ResponseBody
                 // 当前类的所有方法都必须要返回json
public class JsonController {

    @PostMapping("/requestJson")  // 同上
    public Account requestJson(@RequestBody Account account){
        // 返回的对象
        account.setName("qiuqiu");
        account.setMoney(1d);
        return account;
    }
}    

Restful 风格的 URL(1)简介

介绍

REST全称是Representational State Transfer,中文意思是表现层状态转移。 它首次出现在2000年Roy Fielding的博士论文中,Roy Fielding是HTTP规范的主要编写者之一。 他在论文中提到:“我这篇文章的写作目的,就是想在符合架构原理的前提下,理解和评估以网络为基础的应用软件的架构设计,得到一个功能强、性能好、适宜通信的架构。REST指的是一组架构约束条件和原则。” 如果一个架构符合REST的约束条件和原则,我们就称它为RESTful架构。

REST本身并没有创造新的技术、组件或服务,而隐藏在RESTful背后的理念就是使用Web的现有特征和能力, 更好地使用现有Web标准中的一些准则和约束。虽然REST本身受Web技术的影响很深, 但是理论上REST架构风格并不是绑定在HTTP上,只不过目前HTTP是唯一与REST相关的实例。 所以我们这里描述的REST也是通过HTTP实现的REST。

简单来说,

  1. restful就是一个url地址。是一种url地址的编写风格。

  2. restful风格的url地址的特点:更简洁、更易于实现缓存、更有层次感。

  3. 举例:

    应用实战:
    在这里插入图片描述
    使用规则:
    在这里插入图片描述

优点

RESTful优点:

  • 结构清晰
  • 符合标准
  • 易于理解
  • 扩展方便
  • 地址简洁
  • 应用广泛

需求

需求:如何实现一个url地址,对应后台多个方法?

传统的方式?

​ http://localhost:8080/user?method=get 查询

​ http://localhost:8080/user?method=save 添加

​ http://localhost:8080/user?method=update 修改

​ http://localhost:8080/user?method=delete 删除

基于restful风格的url实现?

地址:http://localhost:8080/user
查询
    1. 后台方法:get()
    2. 请求方式:get
    3. 后台只处理get请求
添加
    1. 后台方法:save()    
    2. 请求方式:post
    3. 后台只处理post请求
修改
    1. 后台方法:update()
    2. 请求方式:put
    3. 后台只处理put请求    
删除
    1. 后台方法:delete()    
    2. 请求方式:delete
    3. 后台只处理delete请求    

http支持的7种请求方式:
在这里插入图片描述

Restful 风格的 URL(2)基于HiddenHttpMethodFilter 的示例

引入

  1. 如果要用restful风格的url地址,可以直接用,不用做任何操作或配置等。
  2. 配置HiddenHttpMethodFilter与restful没有关系, 主要是为了让页面支持put、delete请求而已。
  3. 总结两点问题:
    1. 使用HiddenHttpMethodFilter过滤请求,主要是为了让页面支持put、delete请求
    2. put、delete请求, 只支持返回json类型的数据

示例

第一步:配置HiddenHttpMethodFilter 过滤器,让页面支持put、delete请求。

	<!--
		配置HiddenHttpMethodFilter 过滤器,让页面支持put、delete请求。
		 1. 页面要有一个form表单,提交方式必须是post
		 2. 表单中要有一个隐藏域(_method),隐藏域就是真正的提交方式
	-->
	<filter>
		<filter-name>hiddenHttpMethodFilter</filter-name>
		<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>hiddenHttpMethodFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

第二步:编写restfulTest.html页面,发送restful风格的url请求。

<input type="hidden" name="_method" value="get"> 这里隐藏域的值,表示具体的请求方式。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>基于restful风格的请求,页面发送:get、post、put、delete请求</title>
</head>
<body>
    <form action="http://localhost:8080/user/100" method="get">
        <input type="submit" value="get 请求">
    </form>

    <form action="http://localhost:8080/user/200" method="post">
        <input type="submit" value="post 请求">
    </form>

    <form action="http://localhost:8080/user/300" method="post">
        <!--添加隐藏域,名称是_method(固定的), 值就是真正的提交方式-->
        <input type="hidden" name="_method" value="put">
        <input type="submit" value="put 请求">
    </form>


    <form action="http://localhost:8080/user/400/500" method="post">
        <!--添加隐藏域,名称是_method(固定的), 值就是真正的提交方式-->
        <input type="hidden" name="_method" value="delete">
        <input type="submit" value="delete 请求">
    </form>
</body>
</html>

第三步:编写UserController,处理同一个地址的不同提交方式的请求

@PathVariable 用于获取restful请求中占位符参数值,赋值给方法的参数。

@Controller
public class RestController {

    /**
     * 只处理get请求,一般执行查询操作。
     * 请求地址:http://localhost:8080/user/100
     * 路径映射:@GetMapping("/user/{id}")
     * 方法参数:
     *         @PathVariable("id")
     *           1. 获取rest请求中占位符参数值
     *           2. value参数,对应路径中的占位符
     */
    @GetMapping("/user/{id}")
    public String list(@PathVariable("id") Integer id){
        System.out.println("查询:" + id);
        return "success";
    }

    /**
     * 只处理post请求,一般执行添加操作。
     * http://localhost:8080/user/200
     */
    @PostMapping("/user/{id}")
    public String add(@PathVariable("id") Integer id){
        System.out.println("添加:" + id);
        return "success";
    }

    /**
     * 只处理put请求,一般执行修改操作。
     * 注意:
     *    put与delete请求,必须返回json字符串,不能跳转。
     */
    @PutMapping("/user/{id}")
    @ResponseBody   // 返回json字符串
    public String update(@PathVariable("id") Integer id){
        System.out.println("修改:" + id);
        return "success";
    }

    /**
     * 只处理delete请求,一般执行删除操作。
     * 注意:
     *   put与delete请求,必须返回json字符串,不能跳转。
     *
     * http://localhost:8080/user/300/400
     */
    @DeleteMapping("/user/{userId}/{deptId}")
    @ResponseBody
    public String delete(
            @PathVariable("userId") Integer userId,
            @PathVariable("deptId") Integer deptId){
        System.out.println("删除:" + userId + "," + deptId);
        return "{\"id\":100,\"name\":\"jack\",\"money\":9999}";
    }

}

第四步:测试
在这里插入图片描述

SpringMVC 实现文件上传

原理

springMVC文件上传,就是用到了Apache的文件上传组件实现。简化了过程。

实现

  1. 添加依赖

            <!--引入文件上传的支持包-->
            <dependency>
                <groupId>commons-fileupload</groupId>
                <artifactId>commons-fileupload</artifactId>
                <version>1.3.3</version>
            </dependency>
    
  2. 编写uploadFile.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>文件上传</title>
    </head>
    <body>
        <form method="post" enctype="multipart/form-data" action="/upload">
            文件:<input type="file" name="imgFile">  <br>
            <input type="submit" value="文件上传">
        </form>
    </body>
    </html>
    
    
  3. 编写UploadController.java

    @Controller
    public class UploadController {
    
        /**
         * 文件上传
         * 请求参数:<input type="file" name="imgFile">
         * 上传需求:
         *    1. 上传到target/upload目录
         *    2. 要保证文件名唯一,文件名 = uuid + 文件后缀
         * 如何实现?
         *    1. 控制器方法通过MultipartFile接收上传的文件对象
         *    2. springmvc.xml中配置文件上传解析器
         */
        @RequestMapping("/upload")
        public String upload(MultipartFile imgFile, HttpServletRequest request) throws Exception {
            // 1. 获取上传的目录的路径
            // 调试观察变量值:springmvc02/target/springmvc02-1.0-SNAPSHOT/upload
            String realPath = request.getServletContext().getRealPath("/upload");
    
            // 2. 每天生成一个新的目录,目录名称就是当前的日期
            String now = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
    
            // 3. 创建目录对象
            File file = new File(realPath,now);
            if (!file.exists()) {
                // 如果目录不存在,就创建目录以及子目录
                file.mkdirs();
            }
    
            // 4. 获取原生的文件名, 上传的文件名: xxx.jpg
            String fileName = imgFile.getOriginalFilename();
            // 5. 处理文件名唯一
            fileName = UUID.randomUUID().toString() + fileName.substring(fileName.lastIndexOf("."));
    
            // 6. 最关键的代码:文件上传,上传到指定的目录中
            // 语义:把imgFile这个文件流,写入到file目录下的fileName文件中。
            imgFile.transferTo(new File(file,fileName));
    
            // 7. 文件上传结束,转发到成功页面
            return "success";
        }
    }
    
  4. 配置springMVC.xml,配置文件上传解析器

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           https://www.springframework.org/schema/context/spring-context.xsd
           http://www.springframework.org/schema/mvc
           https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    
        <!--1. 注解扫描-->
        <context:component-scan base-package="xxx.xxx"/>
    
        <!--2. 配置视图解析器-->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <!--跳转路径的前缀-->
            <property name="prefix" value="/pages/"/>
            <!--跳转路径的后缀-->
            <property name="suffix" value=".jsp"/>
        </bean>
    
        <!--3. 注解驱动-->
        <mvc:annotation-driven/>
    
        <!--4. 把静态资源交给默认sevlet处理(DefaultServlet), 解决拦截请求路径是/静态资源无法访问的问题。-->
        <mvc:default-servlet-handler/>
    
        <!--5. 配置文件上传解析器;注意:这里的id固定,不能写其他名称,springmvc根据对象名称找解析器的。-->
        <bean id="multipartResolver"
              class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
            <!--设置允许上传的文件最大值:10M  (10 * 1024 * 1024)-->
            <property name="maxUploadSize" value="10485760"/>
        </bean>
    </beans>
    

SpringMVC异常处理(一)异常处理思路

Java异常分类

|–Throwable

​ |-- Error

​ |-- Exception 检查异常(编译异常)

​ |–RuntimeException 运行时期异常

检查异常: 会给调用者带来麻烦。调用者必须处理异常,不处理异常程序无法继续。

运行时期异常:调用者可以处理异常,也可以不处理异常。更灵活。

项目中如何处理异常

在三层架构中,如果Dao持久层有异常可以抛出到调用方(Service), Service有异常可以抛出到Controller,但是Controller有异常,不建议抛出到客户端(用户), 因为用户看不懂异常, 无法进行处
理会导致用户体验非常差。

异常处理的原则:出现异常要给用户友好提示,自动跳转到友好提示页面。

  1. 代码层面:

    dao 抛异常

    service 抛异常

    web 处理异常

    能够控制器每个方法都写try…catch… 太麻烦!

  2. 异常处理方案

    2.1 表现层,处理异常,写try.catch 不推荐

    2.2 自定义异常过滤器器 (处理所有的异常)

    举例如下:

    public class ExceptionFilter implements Filter {
       public void doFilter(ServletRequest request, ServletResponse response,
    FilterChain chain){
           try{
               chain.doFilter(request, response);
           }catch(Exception e){
               // 异常处理
           }
       }
    }
    

    2.3 使用框架自带的异常处理机制。更加简单。

SpringMVC异常处理(二)异常处理实现

实现步骤

通过SpringMVC自定义异常类,可以自动捕获到控制器中的所有方法的异常。

  1. 写控制器 (模拟异常,int i=1/0;)
  2. 测试
  3. 如果没有用springMVC的异常处理,传统的异常处理如何实现?
  4. 改为springMVC提供的异常处理机制实现异常处理

实现

1、写控制器 (模拟异常,int i=1/0;)

@Controller
public class UserController {

    @RequestMapping("save")
    public String save(HttpServletRequest request){
        System.out.println("保存用户...");
        // 模拟异常
        int i= 1/0;
        return "success";
    }
}

2、测试: 直接打印异常到浏览器

3、如果没有用springMVC的异常处理,传统的异常处理如何实现?
在这里插入图片描述

@Controller
public class UserController {

    @RequestMapping("save")
    public String save(HttpServletRequest request){
        try {
            System.out.println("保存用户...");
            // 模拟异常
            int i= 1/0;
        } catch (Exception e) {
            e.printStackTrace();
            // 保存错误信息,进入错误页面!
            request.setAttribute("error","对不起,系统忙,请稍后再试!或者联系管理员电话:xxxxxx");
            return "error";
        }
        return "success";
    }
}

4、改为springMVC提供的异常处理机制实现异常处理

修改控制器,不写try…catch,希望出现异常,自动跳转到异常页面:

@Controller
public class UserController {

    @RequestMapping("save")
    public String save(HttpServletRequest request){
        System.out.println("保存用户...");
        // 模拟异常
        int i= 1/0;
        return "success";
    }
}

自定义全局异常:

/**
 * 自定义全局异常:
 *  1. 写一个类实现HandlerExceptionResolver接口即可;
 *  2. 创建全局异常类,且加入容器, 自动生效
 *  3. 当控制器方法(service、dao)出现异常时候,自动来到全局异常处理类的resolveException()处理异常的方法
 */
@Component
public class CustomException implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(
            HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex) {
        // 打印异常
        ex.printStackTrace();

        ModelAndView mv = new ModelAndView();
        // 存储数据
        mv.addObject("error","对不起,系统忙,请稍后再试!或者联系管理员电话:120");
        // 设置转发的页面名称
        mv.setViewName("error");
        return mv;
    }
}

SpringMVC拦截器(一)拦截器作用

过滤器作用

  1. 拦截请求

  2. 可以拦截所有的请求(/*):jsp、html、css、js、img、servlet、controller

  3. 作用

    处理请求之前做一些初始化操作

    请求处理完成后,也可以做一些其他操作。如:释放资源。

  4. 是servlet的技术,应用范围比较广

拦截器

什么是拦截器

springmvc框架中的拦截器,相当于web阶段学习的过滤器(filter),可以实现前置增强和后
置增强功能。在springmvc框架中,拦截器可以对处理器方法执行预处理(前置增强),和执行
后处理(后置增强)。

拦截器的作用

  • Spring MVC 的处理器拦截器类似于Servlet 开发中的过滤器Filter
  • 用于对处理器进行预处理 和 后处理。
  • 用户可以自己定义一些拦截器来实现特定的功能

拦截器与过滤器区别

  • springmvc的拦截器是属于springmvc的技术,只能在使用springmvc的技术上,才可以使用其拦截器

  • 拦截的请求: 主要是拦截springmvc的请求

  • 他们的作用基本上是一样的
    在这里插入图片描述

SpringMVC拦截器(二)自定义拦截器

使用步骤

  1. 写一个普通java类,实现接口HandlerInterceptor
  2. springMVC中,配置拦截器

示例代码

在这里插入图片描述

1、写一个普通java类,实现接口HandlerInterceptor

/**
 * 自定义拦截器
 */
@Component
public class DemoInterceptor implements HandlerInterceptor {
    /**
     * preHandle()
     * 1. 请求的预处理操作,在执行控制器方法之前先执行
     * 2. 返回值
     *    true  默认返回true, 表示放行,执行下一个拦截或控制器的方法
     *    false 不放行,不执行后面所有。
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        System.out.println("1. DemoInterceptor.preHandle()");
        return true;
    }

    /**
     * postHandle()
     * 1. 当preHandle()返回true的时候才执行
     * 2. 当控制器方法正常执行结束后才执行. 控制器方法有异常就不执行
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        System.out.println("3. DemoInterceptor.postHandle()");
    }

    /**
     * afterCompletion
     * 1. 当preHandle()返回true的时候才执行
     * 2. 最后执行的方法,不管控制器方法时候有异常都执行
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        System.out.println("4.DemoInterceptor.afterCompletion()");
    }
}

2、springMVC中,配置拦截器

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--1. 注解扫描-->
    <context:component-scan base-package="xxx.xxx"/>

    <!--2. 配置视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--跳转路径的前缀-->
        <property name="prefix" value="/pages/"/>
        <!--跳转路径的后缀-->
        <property name="suffix" value=".jsp"/>
    </bean>

    <!--3. 注解驱动-->
    <mvc:annotation-driven/>

    <!--4. 把静态资源交给默认sevlet处理(DefaultServlet), 解决拦截请求路径是/静态资源无法访问的问题。-->
    <mvc:default-servlet-handler/>

    <!--5. 配置文件上传解析器;注意:这里的id固定,不能写其他名称,springmvc根据对象名称找解析器的。-->
    <bean id="multipartResolver"
          class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!--设置允许上传的文件最大值:10M  (10 * 1024 * 1024)-->
        <property name="maxUploadSize" value="10485760"/>
    </bean>

    <!--6. 拦截器的配置-->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--拦截的路径. /** 拦截所有路径及其子路径-->
            <mvc:mapping path="/**"/>
            <!--注入自定义的拦截器-->
            <ref bean="demoInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
</beans>

测试

观察执行顺序:
在这里插入图片描述

执行流程

在这里插入图片描述

SpringMVC拦截器(三)多个拦截器执行

实现步骤

1、编写自定义的拦截器类

@Component
public class DemoInterceptor2 implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        System.out.println("DemoInterceptor2.preHandle()");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        System.out.println("DemoInterceptor2.postHandle()");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        System.out.println("DemoInterceptor2.afterCompletion()");
    }
}

2、配置拦截器

    <!--6. 拦截器的配置-->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--拦截的路径. /** 拦截所有路径及其子路径-->
            <mvc:mapping path="/**"/>
            <!--注入自定义的拦截器-->
            <ref bean="demoInterceptor"/>
        </mvc:interceptor>
        
        <!--配置第二个拦截器-->
        <mvc:interceptor>
            <!--拦截指定的路径-->
            <mvc:mapping path="/save"/>
            <ref bean="demoInterceptor2"/>
        </mvc:interceptor>
    </mvc:interceptors>

在这里插入图片描述




#### 代码

```java
@Controller
@RequestMapping("/order")
public class OrderController {

    //A. 控制器方法中传入servletApi,保存数据
    @RequestMapping("/save1")
    public String save1(HttpServletRequest request){
        request.setAttribute("cn","China");
        return "success";
    }

    //B. 通过控制器方法的返回值ModelAndView
    @RequestMapping("/save2")
    public ModelAndView save2(){
        // 传入参数:转发的页面名称(前缀后缀在视图解析器中配置)
        ModelAndView mv = new ModelAndView("success");
        mv.addObject("cn","China2");
        return mv;
    }

    //C. 控制器方法中通过Map参数存储数据到request域
    @RequestMapping("/save3")
    public String save3(Map<String,Object> map){
        // 存储数据到request域
        map.put("cn","China3");
        return "success";
    }

    //D. 控制器方法中通过Model、ModelMap参数,存储数据到request域
    @RequestMapping("/save4")
    public String save4(Model model){
        // 存储数据到request域
        model.addAttribute("cn","China4");
        return "success";
    }
    //E
    @RequestMapping("/save5")
    public String save5(ModelMap model){
        // 存储数据到request域
        model.addAttribute("cn","China5");
        return "success";
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值