zoukankan      html  css  js  c++  java
  • Spring MVC详细讲解

    一:三层架构和MVC

    1:三层架构

    我们的开发架构一般都是基于两种形式:一种是 C/S 架构,也就是客户端/服务器,另一种是 B/S 架构,也就是浏览器服务器。在 JavaEE 开发中,几乎全都是基于 B/S 架构的开发。那么在 B/S 架构中,系统标准的三层架构包括:表现层、业务层、持久层。三层架构在我们的实际开发中使用的非常多。

    ①:表现层:
      也就是我们常说的web层。它负责接收客户端请求,向客户端响应结果,通常客户端使用http协议请求web 层,web 需要接收 http 请求,完成http 响应。
      表现层包括展示层和控制层:控制层负责接收请求,展示层负责结果的展示。
      表现层依赖业务层,接收到客户端请求一般会调用业务层进行业务处理,并将处理结果响应给客户端。
      表现层的设计一般都使用 MVC 模型。(MVC 是表现层的设计模型,和其他层没有关系)
    ②:业务层:
      也就是我们常说的 service 层。它负责业务逻辑处理,和我们开发项目的需求息息相关。web 层依赖业务层,但是业务层不依赖 web 层。
      业务层在业务处理时可能会依赖持久层,如果要对数据持久化需要保证事务一致性。(也就是我们说的,事务应该放到业务层来控制)
    ③:持久层:
      也就是我们是常说的 dao 层。负责数据持久化,包括数据层即数据库和数据访问层,数据库是对数据进行持久化的载体,数据访问层是业务层和持久层交互的接口,业务层需要通过数据访问层将数据持久化到数据库中。通俗的讲,持久层就是和数据库交互,对数据库表进行曾删改查的。

     2:MVC模型

    MVC(Model View Controller):是模型(model)-视图(view)-控制器(controller)的缩写,是一种用于设计创建 Web 应用程序表现层的模式。MVC 中每个部分各司其职。

    2. Model:
      数据模型,JavaBean的类,用来进行数据封装。 3. View:
      指JSP、HTML用来展示数据给用户 4. Controller:
      用来接收用户的请求,整个流程的控制器。用来进行数据校验等。

    二:Spring MVC概述

     1:什么是SpringMVC

      SpringMVC 是一种基于 Java 的实现 MVC 设计模型的请求驱动类型的轻量级 Web 框架,属于 Spring FrameWork 的后续产品,已经融合在 Spring Web Flow 里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的 MVC 架构,从而在使用 Spring 进行 WEB 开发时,可以选择使用 Spring的 Spring MVC 框架或集成其他 MVC 开发框架,如 Struts1(现在一般不用),Struts2 等。

    SpringMVC 已经成为目前最主流的 MVC 框架之一,并且随着 Spring3.0 的发布,全面超越 Struts2,成为最优秀的 MVC 框架。它通过一套注解,让一个简单的 Java 类成为处理请求的控制器,而无须实现任何接口。同时它还支持RESTful 编程风格的请求。

    2:Spring MVC具体位置

     

     3:Spring MVC优势

    ①:清晰的角色划分,让我们能非常简单的设计出整洁的Web层,进行更简洁的Web层的开发。
    ②:天生与Spring框架集成(如IoC容器、AOP等),是其它 Web 框架所不具备的。
    ③:提供强大的约定大于配置的契约式编程支持(注解开发)。
    ④:利用 Spring 提供的 Mock 对象能够非常简单的进行 Web 层单元测试。 
    ⑤:支持灵活的URL到页面控制器的映射。
    ⑥:非常灵活的数据验证、格式化和数据绑定机制,能使用任何对象进行数据绑定,不必实现特定框架的API。
    ⑦:提供一套强大的JSP标签库,简化JSP开发。
    ⑧:本地化、主题的解析的支持,使我们更容易进行国际化和主题的切换。
    ⑨:更加简单的异常处理。
    ⑩:对静态资源的支持。
    ①①:支持Restful风格。

    三:Spring MVC入门案例

    1:搭建注意事项

    ①:首先创建maven的时候选择模板:maven-archetype-webapp
    ②:如果项目创建特别慢的需要在创建时的New Module界面下的Properties下添加archetypeCatalog internal键值对
    ③:项目构建完成后在main文件下建立java和resources文件夹并通过右击找到Make Directory as指定文件夹类型
    注意:web.xml各版本坐标约束
    ①:web-app 2.3
    <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > ②:web-app 2.4 <web-app id="WebApp_9" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> ③:web-app 2.5 <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> jdk版本1.5以上 ③:web-app 3.0 <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> jdk版本1.6以上,开始支持jsp-config配置 ④:web-app 3.1 <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> jdk版本1.7以上

    2:简单搭建并测试

    <!--配置信息-->
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
            <!--设置版本锁定  下面的Spring都引用这个版本-->
            <spring.version>5.2.6.RELEASE</spring.version>
        </properties>
    
        <!--坐标导入-->
        <dependencies>
            <!--Spring核心包-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <!--导入Spring对web的支持-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-web</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <!--导入Spring MVC坐标-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <!--导入Servlet-->
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>servlet-api</artifactId>
                <version>2.5</version>
            </dependency>
            <!--导入JSP-->
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>jsp-api</artifactId>
                <version>2.0</version>
            </dependency>
        </dependencies>
    pom.xml坐标导入
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation=" http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/mvc
           http://www.springframework.org/schema/mvc/spring-mvc.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd">
        <!--开启注解-->
        <context:component-scan base-package="cn.xw"></context:component-scan>
        <!--编写视图解析器-->
        <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="prefix" value="/WEB-INF/page/"></property>
            <property name="suffix" value=".jsp"></property>
        </bean>
        <!--开启mvc注解功能-->
        <mvc:annotation-driven></mvc:annotation-driven>
    </beans>
    编写配置文件resource:springmvc.xml
    <web-app
            version="3.0"
            xmlns="http://java.sun.com/xml/ns/javaee"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
        <!--配置前端控制器-->
        <!--简单介绍
            DispatcherServlet:它是前端控制器,最重要的模块,后面会介绍
            init-param标签:它是用来加载配置的springmvc.xml配置文件的,
            load-on-startup标签:表示服务器一启动就加载配置web.xml,然后读取配置文件
            /:在这代表任何请求都会经过前端控制器,由前端控制器来控制请求分发
        -->
        <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>
    编写web.xml
    package cn.xw.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    /**
     * Controller类 
     */
    @Controller(value="indexController")
    public class IndexController {
        //注解路径
        @RequestMapping(path = "/indexPage")
        public String indexPage(){
            System.out.println("访问完成");
            //跳转success路径jsp资源,因为我在视图解析器配置了 可以很快找到
            //<property name="prefix" value="/WEB-INF/page/"></property>    代表跳转当前文件夹下
            //<property name="suffix" value=".jsp"></property>  代表以.jsp后缀的文件
            return "success";
        }
    }
    在java文件夹下编写indexController
    #####index.jsp  这里建议重写建一个index.jsp覆盖之前的
    
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
        <a href="./indexPage">开始访问咯</a>
    </body>
    </html>
    
    
    #####在/WEB-INF/page/下建一个success.jsp页面
    
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
        <h5>欢迎大家访问</h5>
    </body>
    </html>
    页面编写

     四:Spring MVC架构

     其实Spring MVC的架构主要是由各个模块来划分的,接下来我就先和大家介绍一下Spring MVC的各模块角色再后分析执行流程

    1:Spring MVC清晰的模块划分

    前端控制器(DispatcherServlet)
    请求到处理器映射(HandlerMapping)
    处理器适配器(HandlerAdapter)
    视图解析器(ViewResolver)
    处理器或页面控制器(Controller)
    验证器( Validator)
    命令对象(Command 请求参数绑定到的对象就叫命令对象)
    表单对象(Form Object 提供给表单展示和提交到的对象就叫表单对象)。

    2:Spring MVC 各模块执行流程

    这里我参考了一篇不错的文章

    五:Spring MVC请求属性详细用法

    1:@RequestMapping注解

    1:RequestMapping注解的作用是建立请求URL和处理方法之间的对应关系
    2: RequestMapping注解可以作用在方法和类上
      ①:作用在类上:第一级的访问目录
      ②:作用在方法上:第二级的访问目录
      ③:细节:路径可以不编写 / 表示应用的根目录开始
    3. RequestMapping的属性
      ①:path和value:这2个是一样的,都是指定访问路径
      ②:method:指定该方法的请求方式
      ③:params:指定限制请求参数的条件
      ④:headers:发送的请求中必须包含的请求头

    @Controller(value="indexController")
    @RequestMapping(value="user")
    public class IndexController {
        //注解路径
        @RequestMapping(path = "/indexPage",params = {"name=Tom","password"},method = {RequestMethod.GET,RequestMethod.POST},headers = {"accept"})
        public String indexPage(){
            System.out.println("请求访问");
            return "success";
        }
    }
    ①:@RequestMapping作用在方法上就是一级路径/user @RequestMapping作用在方法上就是二级路径;前两者合起来就是/user/indexPage
    ②:params指定了name必须是Tom和一个password,所以我们的uri路径就变成./user/indexPage?name=Tom&password=123
    ③:根前面的路径以及可以了,但是指定了method必须以get/post两个请求访问,而且还得携带请求头accept

    2:@RequestParam注解和请求参数绑定

    ①:@RequestParam注解

      先看一个简单的不使用@RequestParam的注解案例可以对下一个案例扩展,这个案例可以直接获取前台发来的name属性,这边的方法参数也可以获取,因为2个name相对应

    @Controller(value="indexController")
    @RequestMapping(value="/user")
    public class IndexController {
        //保存用户
        @RequestMapping(path="/save",method = {RequestMethod.GET,RequestMethod.POST})
        public String saveUser(String name){
            System.out.println("打印name:"+name);
            return "success";
        }
    }

    访问代码:
    <a href="./user/save?name=Tom">开始访问咯</a>

      那么问题来了,如果2个要不一样则必须使用@RequestParam注解完成映射,这里先简单介绍一下这个注解

    1:RequestParam的属性
        ①:name和value:这2个一样,映射表单信息,严格区分大小写
        ②:required:默认true,true代表必须获取此属性,false可有可无
    @Controller(value="indexController")
    @RequestMapping(value="/user")
    public class IndexController {
        //保存用户
        @RequestMapping(path="/save",method = {RequestMethod.GET,RequestMethod.POST})
        public String saveUser(@RequestParam(value = "userName" , required = false) String name){
            System.out.println("打印name:"+name);
            return "success";
        }
    }
    
    访问路径:<a href="./user/save?userName=Tom">开始访问咯</a>

      这前面介绍的都是简单的类型映射,那平常传递的都是表单,所以我们接收的话就使用JavaBean对象来接收

    ②:请求参数绑定对象

      注:请求参数必须与javaBean对象里面的参数挨个对应,否则无法映射,如果不一样只能使用上面的@RequestParam注解,还有就是如果实体类里面包含别的自己定义的实体类,那么被包含的那个实体类必须有无参构造方法

    //宠物类
     public class Dog {                           
        private String name;//小狗姓名            
        private String color;//小狗颜色     
        //下面代码省略
    }  
    //用户类
    public class User { 
        private Integer id;     //id
        private String name;    //姓名
        private String password;//密码
        private Date birthday;  //生日
        private Dog dog;        //宠物狗
        //下面代码省略
    }  
    2个实体类
    <form action="./user/save" method="post">
        id:<input type="text" name="id"><br>
        姓名:<input type="text" name="name"><br>
        密码:<input type="password" name="password"><br>
        生日:<input type="text" name="birthday"><br>
        宠物狗姓名:<input type="text" name="dog.name"><br>
        宠物狗颜色:<input type="text" name="dog.color"><br>
        <input type="submit" value="提交">
    </form>
    @Controller(value="indexController")
    @RequestMapping(value="/user")
    public class IndexController {
        //保存用户
        @RequestMapping(path="/save",method = {RequestMethod.GET,RequestMethod.POST})
        public String saveUser(User user){
            System.out.println("打印name:"+user);
            return "success";
        }
    }
    这里我在浏览器输入了如下值:
    id:12
    姓名:安徒生
    密码:••••••
    生日:2018/8/8
    宠物狗姓名:大黄
    宠物狗颜色:黄色
    
    打印控制台是:
    打印name:User{id=12, name='?????????', password='1231', birthday=Wed Aug 08 00:00:00 CST 2018, dog=Dog{name='?¤§é??', color='é??è??'}}

      这里大家都发现了出现了乱码,原因是我们使用post提交方式是有乱码问题的,但是get请求在tomcat8以已经被官方解决了。那乱码怎么解决呢?我们在下一节给大家介绍。还有就是这里的日期必须写yyyy/MM/dd格式的,因为写别的格式系统无法解析,在后面会为大家介绍自定义类型转换器

     ③:复杂类型请求参数绑定

    public class User {
        private String name;                //姓名
        private List<Dog> listDog;          //list方式存储宠物狗
        private Map<String,Dog> mapDog;     //map方式存储宠物狗
        .....
    }
    public class Dog {
        private String name;//小狗姓名
        private String color;//小狗颜色
       .....必须携带空参构造
    }
    实体类更改
    <form action="./user/save" method="get">
        姓名:<input type="text" name="name"><br>
        list宠物狗姓名:<input type="text" name="listDog[0].name"><br>
        list宠物狗颜色:<input type="text" name="listDog[0].color"><br>
        map宠物狗姓名:<input type="text" name="mapDog['dog'].name"><br>
        map宠物狗颜色:<input type="text" name="mapDog['dog'].color"><br>
        <input type="submit" value="提交">
    </form>
    注:除了这2更改,其它代码没有变化
    填写参数:
    姓名:tom
    list宠物狗姓名:tomDog
    list宠物狗颜色:#f00
    map宠物狗姓名:tomDog
    map宠物狗颜色:#f0f
    
    打印结果:打印name:User{name='tom', listDog=[Dog{name='tomDog', color='#f00'}], mapDog={dog=Dog{name='tomDog', color='#f0f'}}}

    3:解决中文乱码问题 POST方式

    出现乱码问题是因为前台和后端的编码不一致所导致的,我们可有使用过滤器的方式对所有的请求拦截过滤放行,使之得到不是乱码的字符

    <!--配置过滤器 解决中文乱码问题-->
        <!--
        public class CharacterEncodingFilter extends OncePerRequestFilter {
                @Nullable
                private String encoding;
                private boolean forceRequestEncoding;
                private boolean forceResponseEncoding;
        查看CharacterEncodingFilter类发现有个encoding,这个就是具体的字符集是什么,我们要设置一下
        /*:代表所有的请求我们都要拦截进行过滤字符集,任何放行
        -->
        <filter>
            <filter-name>encodingFilter</filter-name>
            <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
            <init-param>
                <param-name>encoding</param-name>
                <param-value>utf-8</param-value>
            </init-param>
        </filter>
        <filter-mapping>
            <filter-name>encodingFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    在web.xml下面配置如下代码

    4:自定义类型转换器

      在上面大家可以看出我写日期类型的时候都是yyyy/MM/dd,但是为什么要这么写呢?因为系统只支持这种解析,像我们平常yyyy-MM-dd就不行,但是怎么要让他支持这种写法呢?这就得用SpringMVC给我们留的接口了Converter,其它的实现类有很多都是常见的转换器,但是恰巧没有的可有自己编写

     默认日期类型:yyyy/MM/dd   准备定义为:yyyy-MM-dd      开始编写:

    /**
     *  StringToDateConverter类准备把字符串转换为日期类型
     *  这里实现Converter接口转换器
     */
    public class StringToDateConverter implements Converter<String, Date> {
        @Override
        public Date convert(String s) {
            //创建SimpleDateFormat日期格式对象
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
            Date date = null;
            try {
                //把日期字符串转换为日期类型对象
                date = format.parse(s);
            } catch (ParseException e) {
                e.printStackTrace();
            }
            return date;
        }
    }
    编写StringToDateConverter工具类的转换器
    <!--注册自定义类型转换器-->
        <!--
        public class ConversionServiceFactoryBean implements FactoryBean<ConversionService>, InitializingBean {
            @Nullable
            private Set<?> converters;     //查看它的类里面是存放set类型属性
        -->
        <bean id="conversionServiceFactoryBean" class="org.springframework.context.support.ConversionServiceFactoryBean">
            <property name="converters">
                <!--这下面的set方法是接收set类型,用来存放各种自定义的转换器-->
                <set>
                    <bean id="stringToDateConverter" class="cn.xw.utils.StringToDateConverter"></bean>
                </set>
            </property>
        </bean>
        
        <!--开启mvc注解功能-->
        <!--conversion-service="conversionServiceFactoryBean"   一定要把自己编写的转换器注册-->
        <mvc:annotation-driven conversion-service="conversionServiceFactoryBean"></mvc:annotation-driven>
    更改springmvc.xml配置文件,在下面直接加入

    说到这里和大家说一个简单的日期转换

    public class User {
        private String name;                //姓名
        @DateTimeFormat(pattern = "yyyy-MM-dd")    //直接在实体类对象上面加上此注解,缺陷就是每个类都要设置
        private Date birthday;              //生日
    }

    5:获取原生Response和Request

    其实SpringMVC框架底层还是围绕Servlet规范来的,所有底层还是和我们在学习java web的时候一样,我们可有在SpringMVC框架中获取Request和Response

    //要想获取什么原生对象直接在方法参数写上就可以获取
        @RequestMapping(path = "/indexPrint")
        public String indexPrint(HttpServletRequest request, HttpServletResponse response){
            String name = request.getParameter("name");
            return "success";
        }

    6:@RequestBody注解获取请求体

    属性:
        required:是否必须有请求体
                默认true(必须包含)/false(可以没有请求体)
    注:设置true就代表肯定是POST提交,设置false代表get/post提交都行
    <form action="./user/save" method="POST">
        姓名:<input type="text" name="name"><br>
        密码:<input type="password" name="password"><br>
        地址:<input type="text" name="address">
        <input type="submit" value="提交">
    </form>
    表单提交代码
    //保存用户
        @RequestMapping(path="/save",method = {RequestMethod.GET,RequestMethod.POST})
        public String saveUser(@RequestBody(required = true) String body){
            System.out.println("打印请求体:"+body);
            return "success";
        }
    //打印请求体:name=%E5%AE%89%E5%BE%92%E7%94%9F&password=123123&address=%E5%AE%89%E5%BE%BD%E5%85%AD%E5%AE%89

    这里看这都乱码了,其实并不是乱码,这只是在POST传输的时候会对中文数据进行了操作,我们只需要把数据解码就行了

        //保存用户
        @RequestMapping(path="/save",method = {RequestMethod.GET,RequestMethod.POST})
        public String saveUser(@RequestBody(required = true) String body){
    
            String decode = null;
            try {
                //解码  以utf-8解码
                decode = URLDecoder.decode(body, "utf-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            System.out.println("解码后 打印请求体:"+decode);
            System.out.println("未解码 打印请求体:"+body);
            return "success";
            
            //解码后 打印请求体:name=安徒生&password=2123123&address=安徽六安
            //未解码 打印请求体:name=%E5%AE%89%E5%BE%92%E7%94%9F&password=2123123&address=%E5%AE%89%E5%BE%BD%E5%85%AD%E5%AE%89
        }

    7:@PathVariable注解

    作用:用于绑定url占位符
    属性介绍:
        value和name一样:表示占位符名称
        required:表示是否必须包含
    @RequestMapping(path = "/indexPrint/{id}")
        public String indexPrint(@PathVariable(name = "id",required = true) int ID){
            System.out.println("打印ID:"+ID);
            return "success";
        }
    
    访问a标签:<a href="./user/indexPrint/20">开始访问咯</a>

    8:@RequestHeader和@CookieValue注解

    两者都有name和value属性:获取指定数据;前者是获取请求头,后者是获取请求的Cookid某个值

        @RequestMapping("/getHeaderAndCookie")
        public String getHeaderAndCookie(@RequestHeader(name="accept") String accept,@CookieValue(name="JSESSIONID") String cookie){
            System.out.println("Accept:"+accept);
            System.out.println("JSESSIONID:"+cookie);
            //Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
            //JSESSIONID:B23DEECE1C365D24C843A5F49EAE8DFD
            return "success";
        }

    9:@ModelAttribute注解

     此注解有2种用法分别是在方法上和方法参数上,它的主要功能是来处理前台表单传递的数据不完整进行操作,总的来说,加上这个注解在方法上优于其它方法先执行,下面我来介绍2种使用方法

    public class User {
        private int id;
        private String name;
        private String address;
    。。。
    }
    实体类更改

    ①:作用在方法上

    <form action="./user/save" method="POST">
        id:<input type="text" name="id"><br>        <%--用来输入id的--%>
        姓名:<input type="text" name="name"><br>    <%--用来输入姓名的--%>
        <%--地址:<input type="text" name="address">--%>
        <input type="submit" value="提交">
        <%--其实实体类上有id、姓名、地址3个属性,但是我恰巧没设置地址输入框--%>
    </form>
     //保存的Controller方法    后执行
        @RequestMapping(path = "/save")
        public String save(User user) {
            System.out.println("打印对象:"+user);
            System.out.println("执行完成");
            return "success";
        }
    
        //注解在方法上@ModelAttribute 优先执行
        @ModelAttribute
        public User holdFun(User user) {
            System.out.println("先执行....");
            //对前端未设置的地址来进行操作  真实开发中是查询数据库的
            user.setAddress("安徽六安");
            return user;
        }
    输入框输入:
        id:12
        姓名:安徒生
        提交按钮
    打印值:
        先执行....
        打印对象:User{id=12, name='安徒生', address='安徽六安'}
        执行完成

    ②:作用在参数上

    介绍:作用在方法上和作用在参数上的唯一区别就是,前者有返回值后者不带放回值

    //保存的Controller方法
        @RequestMapping(path = "/save")
        public String save(@ModelAttribute(value = "mapUser") User user) {
            System.out.println("打印对象:"+user);
            System.out.println("执行完成");
            return "success";
        }
    
        //注解在方法上@ModelAttribute 优先执行
        @ModelAttribute
        public void holdFun(User user,Map<String,User> map) {
            System.out.println("先执行....");
            //对前端未设置的地址来进行操作  真实开发中是查询数据库的
            user.setAddress("安徽六安");
            //因为没有返回值了,所有在方法参数上设置一个map,
            map.put("mapUser",user);
        }

    ③:注意事项

      在使用@ModelAttribute的时候,一般用于前端form表单未封装的数据进行后期补充操作(说白了就是前端没有指定的输入框),如果前端指定了输入框,而且不输入值,这个到后端这个注解下是不可以操作的,如果前端传来地址为空(前端传往后端的数据都是空串如:“ ”),并且在后台设置值也是不生效的

    @ModelAttribute
        public void holdFun(User user,Map<String,User> map) {
            System.out.println("先执行....");
            //对前端未设置的地址来进行操作  真实开发中是查询数据库的
            user.setAddress("安徽六安");
            user.setName("阿布");    //这个姓名封装是不起效果的
            //因为没有返回值了,所有在方法参数上设置一个map,
            map.put("mapUser",user);
        }
    
    <form action="./user/save" method="POST">
        id:<input type="text" name="id"><br>        <%--用来输入id的--%>
        姓名:<input type="text" name="name"><br>   因为前端有这个输入框了
        <%--地址:<input type="text" name="address">--%>
        <input type="submit" value="提交">
        <%--其实实体类上有id、姓名、地址3个属性,但是我恰巧没设置地址输入框--%>
    </form>

    10:@SessionAttribute注解

    用于多次执行器方法间的参数共享

    @Controller(value = "indexController")
    @RequestMapping(value = "/user")
    @SessionAttributes(value = {"name"},types = String.class)//这里必须写model添加的数据
    public class IndexController {
        //添加session数据
        @RequestMapping(path = "addAttribute")
        public String addAttribute(Model model) {
            model.addAttribute("name", "张三");
            return "success";
        }
    
        //获取域session数据
        @RequestMapping(path="getAttribute")
        public String getAttribute(ModelMap model){
            System.out.println(model.getAttribute("name"));
            return "success";
        }
    
        //这里说明一下  要想删除Session里面的数据必须使用SessionStatus接口及实现类
        @RequestMapping(path = "delAttribute")
        public String delAttribute(SessionStatus status){
            status.setComplete();
            return "success";
        }
    }

     六:Spring MVC响应属性详细用法

       第五章节已经对请求的基本操作已经做了一个介绍和代码演示,我们接下来将来学习一下Spring MVC的响应操作,我们还是以第三章说的入门代码为例,在上面进行MVC的响应代码编写。

    1:响应String的返回类型

      每个Controller方法返回的字符串类型可以理解为逻辑视图名称,说白了就是我们返回的字符串会通过视图解析器进行解析后跳转到指定页面。

    #####Controller类下的java代码
    
     @RequestMapping("/indexPage")
        public String indexPage() {
            System.out.println("访问完成");
            return "success";
        }
    
    #####springmvc.xml下面的代码
     <!--配置视图解析器-->
        <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="prefix" value="/WEB-INF/page/"></property>
            <property name="suffix" value=".jsp"></property>
        </bean>

    通过上面代码大家可以看出返回值类型为String,返回“success”,上面说过,返回后会经过视图解析器,会被解析为:/WEB-INF/page/success.jsp,这视图解析器就是把返回值加上前缀和后缀就可以完成,然后底层就是用我们以前使request.getRequestDispatcher("/WEB-INF/page/success.jsp").forward(request,response);

     ①:响应String的另一种方式之forword

    @RequestMapping("/indexPage")
        public String indexPage() {
            System.out.println("访问完成");
            return "forward:/WEB-INF/page/success.jsp";
        }
    //像这种直接返回字符串以forward方式的是不会经过视图解析器,而是直接调用底层的request转发,这种转发会把方法参数也携带过去
    //,但是session数据必须手动封装放到域中,不会随转发而携带

    ②:响应String的另一种方式之redirect

    @RequestMapping("/indexPage")
        public String indexPage() {
            System.out.println("访问完成");
            return "redirect:/ref.jsp";
        }
    //重定向302,这种重定向的方式是告诉客户端去询问指定目标,但是这种方式不能访问WEB-INF下的资源,所有我在webapp根目录下创建了一个ref.jsp的页面
    //这种重定向的方式不会携带任何数据

    ③:关于返回String的小总结

      其实要简单理解就是返回的任何值都会经过视图解析器,任何加上前缀和后缀,但是返回值为空字符串return "",这个返回后该怎么办呢?其实Spring MVC也为我们提供了默认,默认就是提起当前方法的@RequestMapping下的path或value的值,按照上面的视图解析器会拼接成/WEB-INF/page/indexPage.jsp,它会默认找page下的这个页面

    2:响应void的返回类型

    我们回过头看看响应返回String类型的,它把返回的字符串经过视图解析器进行操作后会生成一个相对路径,那现在没有返回值类型了怎么办呢?不知道大家有没有想到,我用原生request进行转发或者用原生response进行也可以的。

    ①:使用原生requestresponse经常转发和重定向

      //完成转发操作
        @RequestMapping("/testRequest")
        public void testRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("完成原生转发操作");
    
            //这里我可以封装转发的数据
            request.setAttribute("name", "张三");
            request.getRequestDispatcher("/WEB-INF/page/success.jsp").forward(request, response);
        }
    //关于转发:转发后,后面如果有代码将不会执行到后面的代码
    //完成重定向操作 @RequestMapping("/testResponse") public void testResponse(HttpServletRequest request, HttpServletResponse response) throws IOException { System.out.println("完成原生重定向操作"); //第一种方法重定向 //response.setStatus(302); //response.setHeader("location",request.getContextPath()+"/ref.jsp"); //第二种重定向 response.sendRedirect(request.getContextPath() + "/ref.jsp"); }
    //关于重定向:重定向后,如果后面有代码会被执行到

    ②:直接写出使用getWriter

    其实在正常的页面访问很少用到这种方式,大部分都是用于采用异步(Ajax)访问才会使用这个写出json数据

    @RequestMapping(value = "/testGetWriter")
        public void testGetWriter(HttpServletRequest request,HttpServletResponse response) throws IOException {
            //写出中文会乱码,开始解决①:
            //response.setCharacterEncoding("utf-8");
            //response.setHeader("content-type","text/html:charset=utf-8");
            //写出中文会乱码,开始解决②:
            response.setContentType("text/html;charset=utf-8");
            response.getWriter().write("写出一个普通文本到前台");
        }

    3:响应ModelAndView对象(重点

     介绍:从名字上可以看出ModelAndView中的Model代表模型,View代表视图;在之前我们使用构建Model对象来存储域对象(转发域),然后通过返回值为String类型返回视图,然后通过视图解析器解析;可是Spring MVC为我们提供了一个ModelAndView对象,它的作用就是结合了前面的复杂操作,直接返回ModelAndView就可以即设置模型又可以设置视图。

    ####ModelAndView的八种构造函数 (着色为重要构造 经常使用)
    ①:ModelAndView()
    ②:ModelAndView(String viewName)
    ③:ModelAndView(String viewName, @Nullable Map<String, ?> model)                                   
    ④:ModelAndView(View view, @Nullable Map<String, ?> model)
    ⑤:ModelAndView(String viewName, HttpStatus status)
    ⑥:ModelAndView(@Nullable String viewName, @Nullable Map<String, ?> model, @Nullable HttpStatus status)
    ⑦:ModelAndView(String viewName, String modelName, Object modelObject)
    ⑧:ModelAndView(View view, String modelName, Object modelObject)
    
    ####ModelAndView的两种设置视图 (着色为重要方法 经常使用)
    ①:void setViewName(@Nullable String viewName)
    ②:void setView(@Nullable View view)
    
    ####ModelAndView的三种设置模型 (着色为重要方法 经常使用)
    ①:ModelAndView addObject(String attributeName, @Nullable Object attributeValue)
    ②:ModelAndView addObject(Object attributeValue) 
    ③:ModelAndView addAllObjects(@Nullable Map<String, ?> modelMap)
    @RequestMapping("/testModelAndViewA")
        public ModelAndView testModelAndViewA() {
            //使用ModelAndView()构造函数
            ModelAndView model = new ModelAndView();
            //设置模型使用ModelAndView addObject(String attributeName, @Nullable Object attributeValue)
            model.addObject("name", "张三");
            model.addObject("age", 25);
            //设置视图void setViewName(@Nullable String viewName)
            model.setViewName("success");
            return model;
        }
    
        @RequestMapping("/testModelAndViewB")
        public ModelAndView testModelAndViewB() {
            //使用ModelAndView(String viewName)构造函数 初始化就设置了视图
            ModelAndView model=new ModelAndView("success");
            //设置模型:ModelAndView addAllObjects(@Nullable Map<String, ?> modelMap)
            Map<String,Object> map=new HashMap<>();
            map.put("name","张胜男");
            map.put("age",25);
            model.addAllObjects(map);
            return model;
        }
    
        @RequestMapping("/testModelAndViewC")
        public ModelAndView testModelAndViewC() {
            Map<String,Object> map=new HashMap<>();
            map.put("name","张胜男");
            map.put("age",25);
            //使用ModelAndView(String viewName, @Nullable Map<String, ?> model)构造函数全部设置好
            return new ModelAndView("success",map);
        }
    //补充:关于addObject(
    Object attributeValue)这个也是设置模型数据,这个和前面不一样,它的键默认string,值就是自己设置的,多个相同的会后面覆盖前面的,
    //所有这个适合设置单个模型数据;关于模型数据就是我们在学javaweb时候的转发域数据request

    4:响应之Ajax方式(重点

      在前面的3小结中我们都是在说响应之某个页面返回数据,可话说回来,如果我前台是通过Ajax的请求呢?显然前台是要来获取一串json数据的,我们就不能返回页面给它了,所有我们接下来就和大家介绍Ajax如何返回数据。

    ①:简单的环境搭建 (编写或更改之前的index.jsp页面)

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
        <head>
            <title>Title</title>
            <script type="application/javascript" src="js/jquery-1.11.1.min.js"></script>
         <!--引入外部jquery文件-->
    </head> <body> <h2>编写Ajax环境</h2> <button id="btn">开始发送Ajax请求数据</button> <script> $(document).ready(function () { $("#btn").on("click", function () { console.log("按钮点击事件生效"); }); }) </script> </body> </html>

    ②:放行静态文件 在springmvc.xml下加入如下代码

    <!--在webapp文件夹下为js/images/css等静态文件全部放行 不会被前端控制器拦截-->
        <mvc:resources mapping="/js/" location="/js/**"></mvc:resources>
        <mvc:resources mapping="/images/" location="/images/**"></mvc:resources>
        <mvc:resources mapping="/css/" location="/css/**"></mvc:resources>
    <!--本案例只涉及到了js文件下的静态文件放行-->

    编写完这些就可以运行程序了,在浏览器如果遇到如下问题就要看下面解决方案:

    原因:SpringMVC在处理这些静态文件是有一些缺陷的,也是最大的弱点。
    说明:在遇到这个问题的朋友们,也不用急,看看我下面的几个解决方案可以解决百分之90的问题
          这类问题我查阅过很多资料也无从定位到SpringMVC内部为什么对静态资源的弱点,有了解的朋友
          真心希望可以告诉我原理谢谢!!
    
    解决方案一:
        介绍:这就是我们上面写的方案,这个是在SpringMVC 3.0之前发布的解决方案 放行静态资源
        <mvc:resources location="/img/" mapping="/img/**"/> 
        <mvc:resources location="/js/" mapping="/js/**"/>  
        <mvc:resources location="/css/" mapping="/css/**"/>
    
    解决方案二:
        介绍:这个是在SpringMVC 3.0之后发布的一种放行静态资源的方式
        <mvc:default-servlet-handler/>
    
    
    解决方案三:
        介绍:在web.xml里面配置 表示这些是静态资源,本案例只涉及到.js文件,后面为补充
        <servlet-mapping>
            <servlet-name>default</servlet-name>
            <url-pattern>*.js</url-pattern>
        </servlet-mapping>
        <servlet-mapping>
            <servlet-name>default</servlet-name>
            <url-pattern>*.css</url-pattern>
        </servlet-mapping>
        <servlet-mapping>
            <servlet-name>default</servlet-name>
            <url-pattern>*.jpg</url-pattern>
        </servlet-mapping>
        ....缺什么类型就补什么类型
    
    崩溃的解决方案了,也是我自己的解决方案
    ①:选择上面的第一种解决方案的代码写到springmvc.xml后关闭项目(关闭idea),
    ②:找到项目的指定静态文件夹下删除了重新复制过去,后打开idea,启动测试
    ③:再不行就关闭程序,打开配置tomcat,把原来的程序删除重新部署到tomcat上运行
    ④:再不行,教你一招绝对可以,关闭idea软件,玩会游戏看会视频,再次打开idea运行,
    解决SpringMVC配置了静态资源放行后,运行程序还是找不到静态资源的解决方案

    程序可以被测试而且点击按钮后有反应后就开始Ajax请求的操作

     ③:响应Ajax方式

     大家都知道Ajax可以局部刷新,不会导致页面刷新,我在这为大家编写JQuery版的Ajax,首先大家要导入jackson坐标

          <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-core</artifactId>
                <version>2.9.9</version>
            </dependency>
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-databind</artifactId>
                <version>2.9.9</version>
            </dependency>
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-annotations</artifactId>
                <version>2.9.9</version>
            </dependency> 

    导入完坐标后,大家编写一下前端的Ajax请求

    <script>
        /*
        * url:链接地址
        * data:传输的数据
        * contextType:解析的模式
        * dataType:接收的数据类型
        * type:发送数据请求类型POST或GET
        * success:返回服务器端的响应
        * */
        $(document).ready(function () {
            $("#btn").on("click", function () {
                console.log("按钮点击事件生效");
                //编写Ajax请求
                $.ajax({
                    url: "textAjax",
                    type: "post",
                    data: {"name": "张三", "password": "12345"},  //这里简单封装了一些json数据请求到后端
                    contextType: "application/json;charset=utf-8",
                    dataType: "json",
                    success: function (data) {
                        console.log(data);
                    }
                })
            });
        })
    </script>
    在index.jsp页面编写Ajax请求
    @RequestMapping("/textAjax")
        public @ResponseBody Student testAjax(@RequestBody(required = true) String body, HttpServletResponse response){
            System.out.println("获取前台的Ajax请求的请求体:"+body);
            //打印获取前台的Ajax请求的请求体:name=%E5%BC%A0%E4%B8%89&password=12345
            //发现传来的json数据我们也看不懂  那我们要对传来的数据进行解析
            try {
                String decode = URLDecoder.decode(body, "utf-8");
                System.out.println("打印前端传来的数据解析后的样子:"+decode);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
    
            //现在我们往前台发送数据 我就往前台发送一个对象
            Student student=new Student(12,"王二狗","安徽六安");
    
            //一:这是原生的自己解析发送前端
            /*//我们自己把实体类转换为一个Json数据
            //创建Jackson的核心对象
            ObjectMapper mapper=new ObjectMapper();
            String json=null;
            try {
                json = mapper.writeValueAsString(student);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
            //设置reponse响应字符集
            response.setContentType("text/html;charset=utf-8");
            //发送到前端
            try {
                response.getWriter().write(json);
            } catch (IOException e) {
                e.printStackTrace();
            }*/
    
            //二:Spring MVC为我们解决好了封装json数据发送到前端
            //把返回值从void改为Student
            //在返回值前面加上@ReponseBody注解后就可以直接返回对象,这个注解里面帮我们解决了解析工作,我们必须要导入jackson坐标
            return student;
        }

    Ajax和Json的详细介绍

     七:Spring MVC之文件上传

     在准备上传代码编写之前,先导入所需的坐标,还有就是在写文件上传的时候提交必须为post方式,因为文件在上传的时候太大,所有说get是有大小的不适合上传

    <dependency>
                <groupId>commons-fileupload</groupId>
                <artifactId>commons-fileupload</artifactId>
                <version>1.3.1</version>
            </dependency>
            <dependency>
                <groupId>commons-io</groupId>
                <artifactId>commons-io</artifactId>
                <version>2.6</version>
            </dependency>
    准备工作 导入坐标
    <body>
    <h2>编写上传文件环境</h2>
    <%--enctype="multipart/form-data"  文件上传必须要有此属性 把一个大文件分成多部分上传  --%>
    <form action="./testFileUpload" method="post" enctype="multipart/form-data">
        选择文件:<input type="file" name="upload">
        <input type="submit" value="提交">
    </form>
    </body>
    编写index.jsp里面上传文件的环境

    1:原始文件上传

    @RequestMapping("/testFileUpload")
        public String testFileUpload(HttpServletRequest request) throws Exception {
    
            //获取存放上传的文件位置  这里的具体upload文件夹是否存在不能确定
            String path = request.getSession().getServletContext().getRealPath("/upload/");
            //创建file对象
            File file = new File(path);
            //判断当前的file路径是否是一个真实路径,如果为false(!file.exists())就创建出来路径
            if (!file.exists()) {
                file.mkdirs();
            }
    
            //创建磁盘文件工厂模式
            DiskFileItemFactory factory = new DiskFileItemFactory();
            //创建上传文件对象,由工厂模式创建
            ServletFileUpload fileUpload = new ServletFileUpload(factory);
            //让文件上传对象获取我们的request请求的域对象来读取表单
            List<FileItem> fileItems = fileUpload.parseRequest(request);
            //循环request请求表单的每个值
            for (FileItem f : fileItems) {
                //判断当前的值是否是一个文件对象,(f.isFormField()是否是普通表单) 反之不是
                if (!f.isFormField()) {
                    //获取UUID
                    String uuid = UUID.randomUUID().toString().replace("-", "");
                    //使用UUID和当前上传的文件名拼接出一个不会重复的名字
                    String fileName = uuid + f.getName();
                    //写出文件到指定位置
                    f.write(new File(path, fileName));
                    //大于10M我们手动删除,小于10M会在内存缓存创建,然后由垃圾回收器回收
                    f.delete();
                }
            }
            return "success";
        }

    2:使用Spring MVC为我们封装好的文件上传

     在使用Spring MVC上传文件的时候我们要在配置文件springmvc.xml上配置一下我们使用了上传文件功能

    <!--配置文件上传bean-->
        <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
            <property name="maxUploadSize" value="5242880"></property><!--配置最大上传5M单个文件 5*1024*1024-->
            <property name="defaultEncoding" value="utf-8"></property><!--编码方式-->
            <property name="maxInMemorySize" value="4096"></property><!--缓存大小-->
        </bean>
        <!--注:里面的属性可以不配  正常默认也就可以了 其实还有几个其它属性没配了   还有id必须为multipartResolver-->
    一般 bean 的 id 仅作为一个唯一的标识,但是在这里你必须保证 id 是 multipartResolver,其他的还有 localeResolver、themeResolver 等。
    为什么要固定 id 呢?原因是在 SpringMVC 的核心类 DispatcherServlet 中,把这些 bean 的 id 固定了。代码如下:
    public class DispatcherServlet extends FrameworkServlet {
        public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";
        public static final String LOCALE_RESOLVER_BEAN_NAME = "localeResolver";
        public static final String THEME_RESOLVER_BEAN_NAME = "themeResolver";
    ....
    }

    开始编写Controller类的上传文件代码

    @RequestMapping("/testFileUpload")
        public String testFileUpload(HttpServletRequest request, MultipartFile upload) throws Exception {
            //这里必须说明一下MultipartFile upload 的方法名必须和表单文件项的name一样
    
            //获取存放上传的文件位置  这里的具体upload文件夹是否存在不能确定
            String path = request.getSession().getServletContext().getRealPath("/upload/");
            //创建file对象
            File file = new File(path);
            //判断当前的file路径是否是一个真实路径,如果为false(!file.exists())就创建出来路径
            if (!file.exists()) {
                file.mkdirs();
            }
    
            //获取UUID
            String uuid = UUID.randomUUID().toString().replace("-", "");
            //使用UUID和当前上传的文件名拼接出一个不会重复的名字
            System.out.println(upload.getName());
            String fileName = uuid + upload.getOriginalFilename();
            //写出文件到指定位置
            upload.transferTo(new File(path, fileName));
            //Spring MVC会默认帮我们清理缓存
    
            return "success";
        }

    八:Spring MVC之异常处理

    大家应该在写前面的代码的时候是否看到直接在页面上报500的错误,特别不友好,为什么会这样呢?因为在请求后台执行代码的时候,如果在service模块遇到异常的时候,如果没处理异常,就会被系统自动往上抛(Controller)到控制层,这时候的模块会被DispatchService前端控制器收到,可是它也没有设置处理业务,算了抛吧,所有大家就在前台看到异常了。

    ###请求
    <a href="./testException">开启异常之路</a>
    
    ###处理请求
    @RequestMapping("/testException")
        public String testException() {
            System.out.println("开始出现异常");
            //异常  数学运算异常
            int a = 1 / 0;    //出现异常
            return "success";
        }

    上面就是一个出现异常的代码,下面我就来写个解决方式

    /**
     * 自己编写的异常类 继承Exception
     */
    public class MyException extends Exception {
        //异常信息
        private String message;
        //构造方法
        public MyException(String message) {
            this.message = message;
        }
        //获取异常信息
        @Override
        public String getMessage() {
            return message;
        }
        //设置异常信息
        public void setMessage(String message) {
            this.message = message;
        }
    }
    编写一个MyException 继承异常类
        @RequestMapping("/testException")
        public String testException() {
            System.out.println("开始出现异常");
            try {
                //异常  数学运算异常
                int a = 1 / 0;
            } catch (Exception e) {
                throw new ArithmeticException("运算异常");
            }
            return "success";
        }
    前面的Controller代码改造
    #####自己编写一个异常处理类 实现 HandlerExceptionResolver
    
    /**
     * 编写异常处理类
     */
    public class MyExceptionResolver implements HandlerExceptionResolver {
        @Override
        public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
            //创建ModelAndView 模型视图  方便后面携带数据返回
            ModelAndView model=new ModelAndView();
            //创建自己异常类
            MyException myException=null;
            //判断拦截传来的是不是自己定义的异常类型一样     Exception e就是拦截有异常的信息
            if(e instanceof MyException){
                //如果就是本异常直接赋值完事
                myException=(MyException) e;
            }else{
                //否则创建一个自己的异常类
                myException=new MyException("服务器宕机了。。。");
            }
            //设置异常页面
            model.setViewName("error");
            //设置携带的异常数据
            model.addObject("message",myException.getMessage());
            //返回
            return model;
        }
    }
    /**
     * 编写异常处理类
     */
    public class MyExceptionResolver implements HandlerExceptionResolver {
        @Override
        public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
            //创建ModelAndView 模型视图  方便后面携带数据返回
            ModelAndView model=new ModelAndView();
            //设置视图页面
            model.setViewName("error");
            //设置携带的异常数据
            model.addObject("message","服务器宕机啦。。。");
            //返回
            return model;
        }
    }
    为了简化,有需求的删除异常接收处理
    <!--注册异常处理类 放入容器中   在springmvc.xml文件里配置-->
    <bean id="myExceptionResolver" class="cn.xw.utils.MyExceptionResolver" ></bean>

    剩下的编写一个异常页面在page文件夹下,出现问题会根据前面的设置跳转到当前的异常页面

    九:Spring MVC之拦截器

     学过javaweb的朋友都知道过滤器Filter,它是用来处理进行预处理和后处理的;接下来我要说的拦截器也不例外,但是它与过滤器略微有点差别

    过滤器是 servlet 规范中的一部分,任何 java web 工程都可以使用。
    拦截器是 SpringMVC 框架自己的,只有使用了 SpringMVC 框架的工程才能用。
    过滤器在 url-pattern 中配置了 /* 之后,可以对所有要访问的资源拦截。
    拦截器它是只会拦截访问的控制器方法,如果访问的是 jsp,html,css,image 或者 js 是不会进行拦截的。
    总结一点:拦截器是SpringMVC基于AOP的思想的具体应用

    下面我们就来编写一个拦截器类

    基本介绍:
        注:编写自定义拦截器必须实现HandlerInterceptor接口,
        此接口里面的方法都做了默认实现,我们需要重写接口方法
    
    方法:
    ①:boolean preHandle(......)==》前置通知
        1:可以使用request或者response跳转到指定的页面,如不放行则可以跳转到提示页面
        2:return true放行后,会执行下一个拦截器,如果后面没有拦截器了,执行controller中请求的方法。
        3:return false不放行,不会执行controller中的方法。这个时候可以跳转到提示页面,提示用户为什么被拦截
    ②:void postHandle(......)==》后置运行通知
        1:可以使用request或者response跳转到指定的页面
        2:如果指定了跳转的页面,那么请求的controller方法将不会执行
    ③:void afterCompletion(......)==》最终通知
        1:request或者response不能再跳转页面了
    /**
     * 自己编写的拦截器类 实现HandlerInterceptor
     */
    public class OneInterceptor implements HandlerInterceptor {
        //相当与AOP的前置通知
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("请求访问 直接拦截");
            //这里如果是true,被拦截的请求是可以放行的,如果是false,被拦截就不会被放行
            return true;
        }
    
        //相当AOP的后置运行通知
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("向客户端响应结果时被拦截");
        }
    
        //相当AOP最终通知
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("全部操作完成后再执行");
        }
    }

    写完这个拦截器类还不行,我们还得去springmvc.xml配置文件配置拦截器类

    <!--注册拦截器-->
        <mvc:interceptors>
            <!--配置第一个拦截器-->
            <mvc:interceptor>
                <!--配置拦截的位置-->
                <mvc:mapping path="/**"/> 
                <!--自定义拦截器类在什么具体位置-->
                <bean id="one" class="cn.xw.utils.OneInterceptor"></bean>
            </mvc:interceptor>
            <!--....如果有多个拦截器则再下面继续配置-->
        </mvc:interceptors>
        <!--
             <mvc:mapping path="/**"/>  代表经过前端控制器的全部请求都被拦下来
             <mvc:mapping path="/*"/>   代表只拦截根目录下的资源,如/index、/login
             <mvc:mapping path="/user/**"/> 代表拦截user下的全部请求
        -->
    测试代码
    /模拟拦截器
        @RequestMapping("/testInterceptors")
        public String testInterceptors() {
            System.out.println("这给请求方法执行了。。。");
            return "success";
        }
    打印结果:

      请求访问 直接拦截
      这给请求方法执行了。。。
      向客户端响应结果时被拦截
      全部操作完成后再执行

  • 相关阅读:
    hdu 1426(DFS+坑爹的输入输出)
    hdu 1430(BFS+康托展开+映射+输出路径)
    hdu 1664(数论+同余搜索+记录路径)
    BestCoder Round #86 二,三题题解(尺取法)
    hdu 1226(同余搜索)
    poj 1426(同余搜索)
    poj 2251(同余)
    hdu 1044(bfs+dfs+剪枝)
    hdu 1455(DFS+好题+经典)
    安装centos7后不能联网
  • 原文地址:https://www.cnblogs.com/antLaddie/p/12893570.html
Copyright © 2011-2022 走看看