• SpringMVC核心技术


    一、请求转发和重定向

    当处理器对请求处理完毕后,向其它资源进行跳转时,有两种跳转方式:请求转发与重定向。而根据所要跳转的资源类型,又可分为两类:跳转到页面与跳转到其它处理器

    注意,对于请求转发的页面,可以是WEB-INF中页面;而重定向的页面,是不能为WEB-INF中页的。因为重定向相当于用户再次发出一次请求,而用户是不能直接访问 WEB-INF 中资源的

    SpringMVC 框架把原来 Servlet 中的请求转发和重定向操作进行了封装。现在可以使用简单的方式实现转发和重定向。

    forward:表示转发,实现 request.getRequestDispatcher("xx.jsp").forward()

    redirect:表示重定向,实现 response.sendRedirect("xxx.jsp")

    1. 请求转发

    处理器方法返回 ModelAndView 时,需在 setViewName()指定的视图前添加 forward:,且此时的视图不再与视图解析器一同工作,这样可以在配置了解析器时指定不同位置的视图。视图页面必须写出相对于项目根的路径。forward 操作不需要视图解析器。
    处理器方法返回 String,在视图路径前面加入 forward: 视图完整路径

    还是第一个案例中

        /**
         * 处理器方法返回ModelAndView,实现转发forward
         * 语法: setViewName("forward:视图文件完整路径")
         * forward特点: 不和视图解析器一同使用,就当项目中没有视图解析器,不受视图解析器的限制了
         */
    
        @RequestMapping(value = "/doForward.do")
        public ModelAndView doForward(String name , Integer age){
    
            ModelAndView mv = new ModelAndView();
    
            mv.addObject("myname",name);
            mv.addObject("myage",age);
    
    //        mv.setViewName("show");
    
            //显式转发
    //        mv.setViewName("forward:/WEB-INF/view/show.jsp");
    
    
            // 配置了视图解析器,但是这个文件不在view目录之下就可以使用这样的方法,因为forward不受视图解析器的限制
            mv.setViewName("forward:/hello.jsp");
    
            return mv;
        }
    

    2. 请求重定向

    在处理器方法返回的视图字符串的前面添加 redirect:,则可实现重定向跳转

     /**
         * 处理器方法返回ModelAndView,实现重定向redirect
         * 语法:setViewName("redirect:视图完整路径")
         * redirect特点: 不和视图解析器一同使用,就当项目中没有视图解析器
         *
         * 框架对重定向的操作:
         * 1.框架会把Model中的简单类型的数据,转为string使用,作为hello.jsp的get请求参数使用。
         *   目的是在 doRedirect.do 和 hello.jsp 两次请求之间传递数据
         *
         * 2.在目标hello.jsp页面可以使用参数集合对象 ${param}获取请求参数值
         *    ${param.myname}
         *
         * 3.重定向不能访问/WEB-INF资源
         */
    
        @RequestMapping(value = "/doRedirect.do")
        public ModelAndView doRedirect(String name , Integer age){
    
            ModelAndView mv = new ModelAndView();
    
            mv.addObject("myname",name);
            mv.addObject("myage",age);
    
    
            //重定向
            mv.setViewName("redirect:/hello.jsp");
            // 相当于这样,内部作为get请求,通过param可以获取的到
            //http://localhost:8080/08_forword/hello.jsp?myname=lisi&myage=22
    
    
            //重定向不能访问/WEB-INF资源
    //        mv.setViewName("redirect:/WEB-INF/view/show.jsp");
    
            return mv;
        }
    

    二、异常处理

    SpringMVC 框架处理异常的常用方式:使用@ExceptionHandler 注解处理异常

    使用注解@ExceptionHandler 可以将一个方法指定为异常处理方法。该注解只有一个可选属性 value,为一个 Class<?>数组,用于指定该注解的方法所要处理的异常类,即所要匹配的异常

    而被注解的方法,其返回值可以是 ModelAndView、String,或 void,方法名随意,方法参数可以是 Exception 及其子类对象、HttpServletRequest、HttpServletResponse 等。系统会自动为这些方法参数赋值

    1. 异常处理步骤:

    1. 新建maven web项目
    2. 加入依赖
    3. 新建一个自定义异常类 MyUserException , 再定义它的子类NameException ,AgeException
    4. 在controller抛出NameException , AgeException
    5. 创建一个普通类,作用全局异常处理类
      • 在类的上面加入@ControllerAdvice
      • 在类中定义方法,方法的上面加入@ExceptionHandler
    6. 创建处理异常的视图页面
    7. 创建springmvc的配置文件
      • 组件扫描器 ,扫描@Controller注解
      • 组件扫描器,扫描@ControllerAdvice所在的包名
      • 声明注解驱动

    1. 自定义异常类

    MyUserException是父类

    package com.md.exception;
    
    /**
     * @author MD
     * @create 2020-08-14 20:31
     */
    public class MyUserException extends Exception {
        public MyUserException() {
            super();
        }
    
        public MyUserException(String message) {
            super(message);
        }
    }
    
    //-------------------------------
    
    package com.md.exception;
    
    /**
     * 当用户的年龄有异常抛出AgeException
     * @author MD
     * @create 2020-08-14 20:33
     */
    public class AgeException extends MyUserException {
        public AgeException() {
            super();
        }
    
        public AgeException(String message) {
            super(message);
        }
    }
    //----------------------------
    package com.md.exception;
    
    /**
     * 当用户的姓名有异常抛出NameException
     * @author MD
     * @create 2020-08-14 20:32
     */
    public class NameException extends MyUserException {
        public NameException() {
            super();
        }
    
        public NameException(String message) {
            super(message);
        }
    }
    
    

    2. 修改 Controller 抛出异常

        /**
         *此时抛出的异常是下面两个异常的父类
         */
        @RequestMapping(value = "/some.do")
        public ModelAndView doSome(String name , Integer age) throws MyUserException {
    
            ModelAndView mv = new ModelAndView();
    
            mv.addObject("myname",name);
            mv.addObject("myage",age);
            // 根据请求参数抛出异常
            if (!"md".equals(name)){
                throw new NameException("姓名不正确");
    
            }
    
            if (age == null || age > 100){
                throw new AgeException("年龄有误");
            }
    
            mv.setViewName("show");
    
    
            return mv;
        }
    

    3. 定义全局异常处理类

    package com.md.handler;
    
    import com.md.exception.AgeException;
    import com.md.exception.NameException;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.servlet.ModelAndView;
    
    /**
     * @author MD
     * @create 2020-08-14 20:38
     */
    
    
    /**
     * @ControllerAdvice : 控制器增强(也就是说给控制器类增加功能--异常处理功能)
     *  位置:在类的上面。
     *  特点:必须让框架知道这个注解所在的包名,需要在springmvc配置文件声明组件扫描器。
     *  指定@ControllerAdvice所在的包名
     */
    @ControllerAdvice
    public class GlobalException {
    
        //定义方法,处理发生的异常
        /*
            处理异常的方法和控制器方法的定义一样, 可以有多个参数,可以有ModelAndView,
            String, void,对象类型的返回值
    
            形参:Exception,表示Controller中抛出的异常对象。
            通过形参可以获取发生的异常信息。
    
            @ExceptionHandler(异常的class):表示异常的类型,当发生此类型异常时,
            由当前方法处理
         */
    
    
        @ExceptionHandler(value = NameException.class)
        public ModelAndView doNameException(Exception ex) {
            // 处理NameException异常
    
            /*
               异常发生处理逻辑:
               1.需要把异常记录下来, 记录到数据库,日志文件。
                 记录日志发生的时间,哪个方法发生的,异常错误内容。
               2.发送通知,把异常的信息通过邮件,短信,微信发送给相关人员。
               3.给用户友好的提示。
             */
    
            ModelAndView mv = new ModelAndView();
            mv.addObject("msg", "姓名是md,其他用户不能访问");
            mv.addObject("ex", ex);
            mv.setViewName("nameError");
    
            return mv;
        }
    
    
    
    
        @ExceptionHandler(value = AgeException.class)
        public ModelAndView doAgeException(Exception ex) {
            // 处理AgeException异常
    
            /*
               异常发生处理逻辑:
               1.需要把异常记录下来, 记录到数据库,日志文件。
                 记录日志发生的时间,哪个方法发生的,异常错误内容。
               2.发送通知,把异常的信息通过邮件,短信,微信发送给相关人员。
               3.给用户友好的提示。
             */
    
            ModelAndView mv = new ModelAndView();
            mv.addObject("msg", "年龄过大");
            mv.addObject("ex", ex);
            mv.setViewName("AgeError");
    
            return mv;
        }
    
    
        // 这个只能有一个,也就是万能的异常处理方法
        // 处理其他异常,NameException、AgeException之外的异常,也就是除了自定义之外的异常都能处理
        @ExceptionHandler
        public ModelAndView doOtherException(Exception ex) {
            // 处理OtherException异常
    
            ModelAndView mv = new ModelAndView();
            mv.addObject("msg", "404");
            mv.addObject("ex", ex);
            mv.setViewName("defaultError");
    
            return mv;
        }
    
    }
    

    4. 定义异常响应页面

    nameError.jsp

    <%--
      Created by IntelliJ IDEA.
      User: MD
      Date: 2020/8/14
      Time: 20:54
      To change this template use File | Settings | File Templates.
    --%>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
        <h1>nameError.jsp</h1>
    
        <h2>提示:${msg}</h2>
    
        <h2>系统异常信息:${ex.message}</h2>
    </body>
    </html>
    
    
    <h1>ageError.jsp</h1>
    
    <h2>提示:${msg}</h2>
    
    <h2>系统异常信息:${ex.message}</h2>
    
    <%--和上面都一样,这里就省略了--%>
    
    <h1>defaultError.jsp</h1>
    
    <h2>提示:${msg}</h2>
    
    <h2>系统异常信息:${ex.message}</h2>
    

    5. 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">
    
    
        <!--声明组件扫描器-->
        <context:component-scan base-package="com.md.controller"/>
    
    
        <!--声明springmvc框架中的视图解析器,帮助开发人员设置视图文件路径-->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <!--前缀:视图文件的路径-->
            <property name="prefix" value="/WEB-INF/view/" />
    
            <!--后缀:视图文件的扩展名-->
            <property name="suffix" value=".jsp"/>
    
        </bean>
    
    
    
        
        
        <!-- 处理异常的两个步骤
    	1. 注册组件扫描器,也即是@ControllerAdvice注解所在的包名
    	2. 注册注解驱动,
    
    -->
        <context:component-scan base-package="com.md.handler"/>
        <mvc:annotation-driven/>
    
    
    
    </beans>
    

    这样就可以了

    三、拦截器

    SpringMVC 中的 Interceptor 拦截器是非常重要和相当有用的,它的主要作用是拦截指定的用户请求,并进行相应的预处理与后处理。

    • 拦截器是springmvc中的一种,需要实现HandlerInterceptor接口。
    • 拦截器和过滤器类似,功能方向侧重点不同。 过滤器是用来过滤器请求参数,设置编码字符集等工作。
    • 拦截器是拦截用户的请求,做请求做判断处理的。
    • 拦截器是全局的,可以对多个Controller做拦截, 一个项目中可以有0个或多个拦截器, 他们在一起拦截用户的请求。
    • 拦截器常用在:用户登录处理,权限检查, 记录日志。

    1. 拦截器使用步骤

    1. 新建maven web项目
    2. 加入依赖
    3. 创建Controller类
    4. 创建一个普通类,作为拦截器使用
      • 实现HandlerInterceptor接口
      • 实现接口中的三个方法
    5. 创建show.jsp
    6. 创建springmvc的配置文件
      • 组件扫描器 ,扫描@Controller注解
      • 声明拦截器,并指定拦截的请求uri地址

    2. 一个拦截器的执行

    Controller类

        @RequestMapping(value = "/some.do")
        public ModelAndView doSome(String name , Integer age) {
            System.out.println("-------MyController的doSome()");
    
            ModelAndView mv = new ModelAndView();
    
            mv.addObject("myname",name);
            mv.addObject("myage",age);
    
            mv.setViewName("show");
    
            return mv;
        }
    

    还是在之前的项目中,定义个普通类,作为拦截器

    package com.md.handler;
    
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.util.Date;
    
    /**
     * 拦截器类,拦截用户的请求
     * @author MD
     * @create 2020-08-14 21:18
     */
    public class MyInterceptor implements HandlerInterceptor {
    // 快捷键:ctrl + O
        private long btime = 0;
    
    
        /*
         * preHandle叫做预处理方法。
         *   重要:是整个项目的入口,门户。 当preHandle返回true 请求可以被处理。
         *        preHandle返回false,请求到此方法就截止。
         *
         * 参数:
         *  Object handler : 被拦截的控制器对象
         * 返回值boolean
         *   true:请求是通过了拦截器的验证,可以执行处理器方法
         *   执行顺序
                拦截器: preHandle()
                -------MyController的doSome()
                拦截器: postHandle()
                拦截器: afterCompletion()
             *
         *   false:请求没有通过拦截器的验证,请求到达拦截器就截止了。 请求没有被处理
         *   仅仅输出这一句话,
         *      拦截器: preHandle()
         *
         *
         *  特点:
         *   1.方法在控制器方法(MyController的doSome)之前先执行的。
         *     用户的请求首先到达此方法
         *
         *   2.在这个 方法中可以获取请求的信息, 验证请求是否符合要求。
         *     可以验证用户是否登录, 验证用户是否有权限访问某个连接地址(url)。
         *      如果验证失败,可以截断请求,请求不能被处理。
         *      如果验证成功,可以放行请求,此时控制器方法才能执行。
         */
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            btime = System.currentTimeMillis();
            System.out.println("拦截器: preHandle()");
    
    
            // 计算的业务逻辑,根据计算的结果,返回true或者false
    
            //  给浏览器一个反馈
    //        request.getRequestDispatcher("/tips.jsp").forward(request,response);
    //
    //        return false;
    
            return true;
        }
    
    
        /*
             postHandle:后处理方法。
             参数:
              Object handler:被拦截的处理器对象MyController
              ModelAndView modelAndView:处理器方法的返回值
    
              特点:
               1.在处理器方法之后执行的(MyController.doSome())
               2.能够获取到处理器方法的返回值ModelAndView,可以修改ModelAndView中的
               数据和视图,可以影响到最后的执行结果。
               3.主要是对原来的执行结果做二次修正,
    
               ModelAndView mv = MyController.doSome();
               postHandle(request,response,handler,mv);
           */
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("拦截器: postHandle()");
    
            // 对原来的doSome执行的结果进行调整
            if (modelAndView != null){
                modelAndView.addObject("mydate" , new Date());
                modelAndView.setViewName("other");
            }
        }
    
    
        /*
              afterCompletion:最后执行的方法
              参数
                Object handler:被拦截器的处理器对象
                Exception ex:程序中发生的异常
              特点:
               1.在请求处理完成后执行的。框架中规定是当你的视图处理完成后,对视图执行了forward。就认为请求处理完成。
               2.一般做资源回收工作的, 程序请求过程中创建了一些对象,在这里可以删除,把占用的内存回收。
             */
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("拦截器: afterCompletion()");
            long etime = System.currentTimeMillis();
            System.out.println("计算从preHandle到请求处理完成的时间:"+(etime - btime ));
        }
    }
    
    

    创建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">
    
    
        <!--声明组件扫描器-->
        <context:component-scan base-package="com.md.controller"/>
    
    
    
    
        <!--声明springmvc框架中的视图解析器,帮助开发人员设置视图文件路径-->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <!--前缀:视图文件的路径-->
            <property name="prefix" value="/WEB-INF/view/" />
    
            <!--后缀:视图文件的扩展名-->
            <property name="suffix" value=".jsp"/>
    
        </bean>
    
    
    
    
        <!-- 声明拦截器: 拦截器可以一个或者多个-->
        <mvc:interceptors>
            <!--声明第一个拦截器-->
            <mvc:interceptor>
                <!-- 指定拦截的请求url地址, page就是url地址,可以使用通配符 ** , 表示任意的字符,文件或者多级目录-->
                <!-- http://localhost:8080/user/xxx-->
    
                <!--<mvc:mapping path="/user/**"/>-->
    
    
                <mvc:mapping path="/**"/>
                <!--声明拦截器对象-->
                <bean class="com.md.handler.MyInterceptor"/>
            </mvc:interceptor>
        </mvc:interceptors>
    
    </beans>
    

    拦截器中方法与处理器方法的执行顺序

    3. 多个拦截器的执行

    再定义一个拦截器

    package com.md.handler;
    
    public class MyInterceptor implements HandlerInterceptor {
    
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
            System.out.println("111111111111拦截器: preHandle()");
    
    
            return true;
        }
    
    
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("11111111111111111拦截器: postHandle()");
    
    
        }
    
    
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("111111111111111拦截器: afterCompletion()");
    
        }
    }
    //---------------------------------------
    
    package com.md.handler;
    public class MyInterceptor2 implements HandlerInterceptor {
    // 快捷键:ctrl + O
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
            System.out.println("2222222222222222拦截器: preHandle()");
    
    
            return false;
        }
    
    
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("2222222222222222拦截器: postHandle()");
    
    
        }
    
    
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("122222222222222222拦截器: afterCompletion()");
    
        }
    }
    
    //---------------------------------------
    
    

    多个拦截器的注册,在springmvc的配置文件

        <!-- 声明拦截器: 拦截器可以一个或者多个
    
            在框架中保存是在一个ArrayList  , 先声明的在前面,
        -->
        <mvc:interceptors>
            <!--声明第一个拦截器-->
            <mvc:interceptor>
                <mvc:mapping path="/**"/>
                <!--声明拦截器对象-->
                <bean class="com.md.handler.MyInterceptor"/>
            </mvc:interceptor>
    
    
    
            <!--声明第二个拦截器-->
            <mvc:interceptor>
                <mvc:mapping path="/**"/>
                <!--声明拦截器对象-->
                <bean class="com.md.handler.MyInterceptor2"/>
            </mvc:interceptor>
    
        </mvc:interceptors>
    
    
        <!-- 返回的都是true,执行的顺序-->
    
        <!--111111111111拦截器: preHandle()-->
        <!--2222222222222222拦截器: preHandle()-->
        <!-- -&#45;&#45;&#45;&#45;&#45;&#45;MyController的doSome()-->
        <!--2222222222222222拦截器: postHandle()-->
        <!--11111111111111111拦截器: postHandle()-->
        <!--122222222222222222拦截器: afterCompletion()-->
        <!--111111111111111拦截器: afterCompletion()-->
    
    
        <!-- 第一个true,第一个false,执行顺序-->
        <!--111111111111拦截器: preHandle()-->
        <!--2222222222222222拦截器: preHandle()-->
        <!--111111111111111拦截器: afterCompletion()-->
    
    
        <!-- 返回的都是false,执行的顺序-->
        <!--111111111111拦截器: preHandle()-->
    
    </beans>
    

    当有多个拦截器时,形成拦截器链。拦截器链的执行顺序,与其注册顺序一致。需要再次强调一点的是,当某一个拦截器的 preHandle()方法返回 true 并被执行到时,会向一个专门的方法栈中放入该拦截器的 afterCompletion()方法

    4. 拦截器和过滤器的区别

    1. 过滤器是servlet中的对象, 拦截器是框架中的对象
    2. 过滤器实现Filter接口的对象, 拦截器是实现HandlerInterceptor
    3. 过滤器是用来设置request,response的参数,属性的,侧重对数据过滤的,
    4. 拦截器是用来验证请求的,能截断请求。
    5. 过滤器是在拦截器之前先执行的。
    6. 过滤器是tomcat服务器创建的对象、拦截器是springmvc容器中创建的对象
    作者:山丘!

    -------------------------------------------

    你闻讯而来,我大喜过望,我在这等你,你又在哪呢?喜欢的话加一个“关注”呗!

    如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!

  • 相关阅读:
    zigbee设备在Zstack中的体现
    第十一章 多态性练习
    IAR 下载 Fatal error: Failed to identify device. Check connections and/or reset hardware
    第十章 继承与派生练习题
    第九章 类和对象练习题(下)
    代码实现判断主机字节序(大端 or小端 )-- 面试题
    winrar解压时出现诊断信息怎么办?
    在线编译网站(持续更新)
    离线环境升级python及安装模块
    Zabbix监控DB,Tomcat
  • 原文地址:https://www.cnblogs.com/mengd/p/13538396.html
Copyright © 2020-2023  润新知