SpringMVC详解(四)核心技术

文章目录

第四章 SpringMVC核心技术

1.转发和重定向

forward:视图完整路径

redirect:视图完整路径

1.1 java代码

@Controller
public class  MyController {

     * 控制器方法返回的是MOdelAndView实现转发forward
     * 语法: mv.setViewName("forward:视图完整路径")
     *
     * forward特点 : 不和视图解析器一同工作的。就当项目中没有视图解析器。
     *
     * 这种方法的意义在于当show.jsp不在视图解析器规定的路径下比如在webapp目录下 就可以用forward来找到
     * */
         
    @RequestMapping(value = "/doForward.do")
    public ModelAndView doForward(){
        System.out.println("执行了MyController的doSome方法");

        ModelAndView mv = new ModelAndView();
//        添加数据
        mv.addObject("msg","处理了some.do请求");
        mv.addObject("fun","执行了doSome方法");

//        mv.setViewName("forward:/WEB-INF/view/show.jsp");
        mv.setViewName("forward:/hello.jsp");
//        返回结果
        return mv;

    }


     * 当控制器发方法返回ModelAndView实现重定向
     * 语法:mv.setViewName("redirect:视图完整路径")
     * redirect特点:不和视图解析器一同工作,就当项目中没有视图解析器
     *
     * 框架提供的重定向的功能
     * 1.框架可以实现两次请求之间的数据传递,把第一个请求中的Model里面
     *   简单类型的数据,转为字符串,附加到目标页面的后面,做get参数传递。
     *   可以在目标页面中获取参数值使用。
     *
     * 2.在目标页面中,可以使用${para.参数名} 获取参数的值
     * */
         
    @RequestMapping("/doRedirect.do")
    public ModelAndView doRedirect(String name,Integer age){
        System.out.println("doRedirect,name=="+name+",age="+age);
        ModelAndView mv = new ModelAndView();
//        这种无法取到值 因为这个放在了第一个request作用域
        mv.addObject("myname",name);
        mv.addObject("myage",age);
//        这里是第二个request作用域。
        mv.setViewName("redirect:/other.jsp");

//        http://localhost:8080/ch08_forward_redirect/other.jsp?myname=lisi&myage=22

//        重定向不能访问/WEB-INF
//        mv.setViewName("redirect:/WEB-INF/view/other.jsp");
        return mv;
    }

}

1.2 jsp页面other.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>show</title>
</head>

    other.jsp,显示request作用域中的数据<br/>
    <H3>myname数据: ${myname}</H3>
    <h3>myage数据: ${myage}</h3>

    <H3>myname数据: ${param.myname}</H3>
    <h3>myage数据: ${param.myage}</h3>

    <h3>获取get请求中的参数:<%=request.getParameter("myname")%></h3>

</body>
</html>

<html>
<head>
    <title>请求方式</title>
</head>
<body>
    <a href="doForward.do">发起doForward.do的get请求</a>
    <br/>
    <br/>
    <p>重定向redirect</p>
    <form action="doRedirect.do" method="post">
        姓名<input type="text" name="name"><br/>
        年龄<input type="text" name="age"><br/>
        操作<input type="submit" value="提交"><br/>
    </form>
</body>
</html>

2. 异常处理

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

​ 框架使用的是集中的异常处理。把各个Controller中抛出的异常集中到一个地方处理。处理异常的叫做异常处理器。框架中使用两个注解完成异常的集中处理。这样每个controller不用单独处理异常了。注解是:

  1. @ExceptionHandler:放在方法的上面,表示此方法可以处理某个类型的异常。当异常发生时执行这个方法。

  2. @ControllerAdvice :放在类的上面,表示这个类中有异常的处理方法。相当于aop中的@Aspect

    @ControllerAdvice :看作是控制器增强,就是给Controller类增加异常(切面)的处理功能。

SpringMVC详解(四)核心技术

  1. 修改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">
<!--    springmvc的配置文件-->
<!--    声明组件扫描器-->
    <context:component-scan base-package="com.sunny.controller"/>
<!--    声明视图解析器:帮助处理视图-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--        前缀:指定视图文件的路径-->
        <property name="prefix" value="/WEB-INF/view/"/>
<!--        后缀:视图文件的拓展名-->
        <property name="suffix" value=".jsp"/>
    </bean>

<!--    异常处理-->
    <context:component-scan base-package="com.sunny.handle"/>
<!--    注解驱动-->
    <mvc:annotation-driven/>

</beans>
  1. Controller

SpringMVC详解(四)核心技术

@Controller
public class  MyController{

    @RequestMapping(value = "/some.do")
    public ModelAndView doSome(String name,Integer age) throws MyUserException {
        System.out.println("执行了MyController的doSome方法");

//        抛出异常
        if(!"zs".equals(name)){
            throw new NameException("姓名不正确");
        }

        if(age == null|| age.intValue()>80){
            throw new AgeException("年龄太大了");
        }

        ModelAndView mv = new ModelAndView();
//        添加数据
        mv.addObject("myname",name);
        mv.addObject("myage",age);

//        mv.setViewName("forward:/WEB-INF/view/show.jsp");
        mv.setViewName("show");
//        返回结果
        return mv;
    }

}

  1. exception
public class AgeException extends MyUserException {

    public AgeException() {
        super();
    }

    public AgeException(String message) {
        super(message);
    }
}
public class MyUserException extends Exception {
//    ctrl + 0

    public MyUserException() {
        super();
    }

    public MyUserException(String message) {
        super(message);
    }
}
public class NameException extends MyUserException {
//    ctrl + o 重写父类的方法
    public NameException() {
        super();
    }

    public NameException(String message) {
        super(message);
    }
}
  1. handle

SpringMVC详解(四)核心技术

package com.sunny.handle;

import com.sunny.exception.AgeException;
import com.sunny.exception.NameException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

import javax.naming.Name;


 *@ControllerAdvice : 表示当前类是异常处理类。给controller增加功能的
 *              位置 : 在类的上面
 * */
@ControllerAdvice
public class GlobalExceptionHandler {
//    定义处理异常的方法,当异常发生后,执行这个方法
//    方法定义和Controller类中方法的定义


     * 处理NameException类型的异常
     * 参数:
     *   Exception : 表示Controller抛出的异常对象
     *
     * @ExceptionHandler: 表示此方法处理异常
     *         属性:value 表示异常类型
     *         位置:方法的上面
     *
     * */
    @ExceptionHandler(value = NameException.class)
    public ModelAndView doNameException(Exception e){
        System.out.println("doNameException===="+e);
 
         * 发生异常:
         *  1.记录异常,记录到日志文件。
         *  2.发送通知,邮件,短信等等
         *  3.给用户友好的提示
         * */
        ModelAndView mv = new ModelAndView();
        mv.addObject("tips","姓名只能是zs");

        mv.setViewName("nameError");
        return mv;
    }

//    处理AgeException
    @ExceptionHandler(value = AgeException.class)
    public ModelAndView doAgeException(Exception e){

        System.out.println("doAgeException===="+e);
    
         * 发生异常:
         *  1.记录异常,记录到日志文件。
         *  2.发送通知,邮件,短信等等
         *  3.给用户友好的提示
         * */
        ModelAndView mv = new ModelAndView();
        mv.addObject("tips","年龄不能大于80");

        mv.setViewName("ageError");
        return mv;
    }
//    处理其他异常    不添加value值就能处理任何异常
    @ExceptionHandler
    public ModelAndView doOtherException(Exception e){

        System.out.println("doOtherException===="+e);
    
         * 发生异常:
         *  1.记录异常,记录到日志文件。
         *  2.发送通知,邮件,短信等等
         *  3.给用户友好的提示
         * */
        ModelAndView mv = new ModelAndView();
        mv.addObject("tips","请稍后重试");

        mv.setViewName("defaultError");
        return mv;
    }
}

  1. jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    ageError.jsp , ${tips}
</body>
</html>

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>请求方式</title>
</head>
<body>
    <a href="doForward.do">发起doForward.do的get请求</a>
    <br/>
    <br/>
    <p>异常处理</p>
    <form action="some.do" method="post">
        姓名<input type="text" name="name"><br/>
        年龄<input type="text" name="age"><br/>
        操作<input type="submit" value="提交"><br/>
    </form>
</body>
</html>

3.拦截器

​ 拦截器:是springmvc框架中的一种对象,需要实现接口HandlerInterceptor。拦截用户的请求。连接到cintroller的请求。

​ 作用:拦截用户的请求,可以预先对请求做处理,决定是否执行Controller。也可以把多个controller*用的功能定义到拦截器。

特点:

  1. 拦截器可以分为系统拦截器和自定义拦截器。
  2. 一个项目可以有多个拦截器。0,或多个自定义拦截器。
  3. 拦截器侧重拦截用户的请求。
  4. 拦截器是在请求处理之前先执行的。

拦截器的定义:

  1. 创建类实现拦截器接口HandlerInterceptor,实现接口中的方法(3个)。
  2. 在springmvc配置文件中,声明拦截器对象,并指定拦截的uri地址。

3.1 第一个拦截器

package com.sunny.handler;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Date;

/**
 * 拦截器
 * */
public class MyInterceptor implements HandlerInterceptor {
//    ctrl+o


  
     * preHandler:预先处理请求的方法。 总的开关
     *    参数 :
     *      Object handler : 被拦截的控制器对象(MyController)
     *    返回值: boolean
     *      true: 请求是正确的,可以被controller处理的。
     *          MyInterceptor拦截器的preHandle====
     *          执行了MyController的doSome方法
     *          MyInterceptor拦截器的postHandle====
     *          MyInterceptor拦截器的afterCompletion====
     *
     *      false:请求不能被处理,控制器方法不会执行。请求到此截止。
     *          MyInterceptor拦截器的preHandle====
     *
     * 特点:
     *   1.预处理方法他的执行时间:在控制器方法之前先执行的。
     *   2.可以对请求做处理,可以做登录的检查,权限的判断,统计数据等等。
     *   3.决定请求是否执行。
     * */
    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {

        System.out.println("MyInterceptor拦截器的preHandle====");
//        request.getRequestDispatcher("/tips.jsp").forward(request,response);
        return true;
    }

   
     * postHandler:后处理方法
     *    参数:
     *        Object handler : 被拦截的控制器对象(MyController)
     *        ModelAndView mv: 控制器方法的返回值 (请求的执行结果)
     *
     *    特点:
     *        1.在控制器方法之后执行的。
     *        2.能获取到控制器方法的执行结果。可以修改为原来的执行结果。
     *          可以修改数据,也可以修改视图。
     *        3.可以对请求的二次处理。
     *
     *
     * */

    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response,
                           Object handler,
                           ModelAndView mv) throws Exception {
        System.out.println("MyInterceptor拦截器的postHandle====");

//        对请求做二次的处理
        if(mv != null){
//            修改数据
            mv.addObject("mydate",new Date());
//            修改视图
            mv.setViewName("other");
        }

    }


     * afterCompletion:最后执行的方法
     *  参数:
     *      Object handler : 被拦截的控制器对象 (MyController)
     *      Exception ex; 异常对象
     *
     *  特点:
     *      1.在请求处理完成后执行的。
     *       请求处理完成的标志是 视图处理完成。对视图执行forward操作后。
     *
     *      2.可以做程序最后要做的工作,释放内存,清理临时变量。
     *
     *      3.方法的执行条件:
     *          1)当前的拦截器他的preHandle()方法必须执行。
     *          2)preHandler()必须返回true
     *
     * */

    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response,
                                Object handler,
                                Exception ex) throws Exception {
        System.out.println("MyInterceptor拦截器的afterCompletion====");

//        获取数据
        HttpSession session = request.getSession();
        Object attr = session.getAttribute("attr");
        System.out.println("attr="+attr);

//        删除数据
        session.removeAttribute("attr");

//        确定数据是否删除
        attr = session.getAttribute("attr");
        System.out.println("删除后,再次检查数据==="+attr);
    }

}

配置文件:springmvc.xml中添加

<!--    声明拦截器-->
    <mvc:interceptors>
<!--        声明第一个拦截器-->
        <mvc:interceptor>
<!--            指定拦截器的拦截地址
                path:拦截的uri地址,使用 ** 通配符。
                     例如:path = "/user/**"
                     http://localhost:8080/user/listUser.do
                     http://localhost:8080/user/query/queryUser.do
-->
            <mvc:mapping path="/**"/>
<!--            指定使用的拦截器-->
            <bean class="com.sunny.handler.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

1)preHandle 中可以进行 登录验证 和 权限判断。

当你的preHandle返回true,执行结果:

MyInterceptor拦截器的preHandle====
执行了MyController的doSome方法
MyInterceptor拦截器的postHandle========
MyInterceptor拦截器的afterCompletion====

请求的执行顺序:用户 some.do ---- preHandle-----doSome----postHandler----afterComplietion

当你的preHandle返回false,执行结果:

false:请求不能被处理,控制器方法不会执行。请求到此截止。
MyInterceptor拦截器的preHandle====
    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {

        System.out.println("MyInterceptor拦截器的preHandle====");
        request.getRequestDispatcher("/tips.jsp").forward(request,response);
        return false;
    }

2)postHandle 对请求做二次的处理

    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response,
                           Object handler,
                           ModelAndView mv) throws Exception {
        System.out.println("MyInterceptor拦截器的postHandle====");

//        对请求做二次的处理
        if(mv != null){
//            修改数据
            mv.addObject("mydate",new Date());
//            修改视图
            mv.setViewName("other");
        }

    }

4)afterCompletion 最后执行

    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response,
                                Object handler,
                                Exception ex) throws Exception {
        System.out.println("MyInterceptor拦截器的afterCompletion====");

//        获取数据
        HttpSession session = request.getSession();
        Object attr = session.getAttribute("attr");
        System.out.println("attr="+attr);

//        删除数据
        session.removeAttribute("attr");

//        确定数据是否删除
        attr = session.getAttribute("attr");
        System.out.println("删除后,再次检查数据==="+attr);
    }

MyInterceptor拦截器的preHandle====
执行了MyController的doSome方法
MyInterceptor拦截器的postHandle====
MyInterceptor拦截器的afterCompletion====
attr=在controller中增加的临时数据
删除后,再次检查数据===null

3.2 多个拦截器

​ 使用两个拦截器,主要看拦截器的执行顺序,以及按个方法控制请求的执行。

1)两个拦截器,第一个preHandler=true,第二个拦截器preHandler=true

MyInterceptor111拦截器的preHandle====
MyInterceptor222拦截器的preHandle====
执行了MyController的doSome方法
MyInterceptor222拦截器的postHandle====
MyInterceptor111拦截器的postHandle====
MyInterceptor222拦截器的afterCompletion====
MyInterceptor111拦截器的afterCompletion====

请求的执行顺序:

​ 用户some.do----拦截器1的preHandle-----拦截器2preHandle-----控制器doSome-------拦截器2postHandle—拦截器1的postHandle------拦截器2的afterCompletion-----拦截器1的afterCompletion。

2)两个拦截器,第一个preHandler=true,第二个拦截器preHandler=flase

MyInterceptor111拦截器的preHandle====
MyInterceptor222拦截器的preHandle====
MyInterceptor111拦截器的afterCompletion====

3)两个拦截器,第一个preHandler = false,第二个拦截器preHandler = true | flase

MyInterceptor111拦截器的preHandle====

为什么要使用多个拦截器?

  1. 把验证功能分散到独立的拦截器、每个拦截器做单一的验证处理。
  2. 组合多个拦截器。

总结:

​ 多个拦截器,串在一个链条上的。多个拦截器和一个控制器对象在一个链条上,框架中使用HandlerExecutionChain(处理器执行链),表示一个执行链条

public class HandlerExecutionChain {
    
    // 存放控制器对象的,MyController
    private final Object handler;
    @Nullable
    private HandlerInterceptor[] interceptors;// 存放多个拦截器对象的。MyInterceptor 1, 2
    @Nullable
    private List<HandlerInterceptor> interceptorList;
    private int interceptorIndex;

    public HandlerExecutionChain(Object handler) {
        this(handler, (HandlerInterceptor[])null);
    }
}

拦截器怎么实现1,2,2,1的执行顺序,遍历 HandlerInterceptor[] interceptors 数组

HandlerInterceptor[] interceptors = {MyInterceptor1,MyInterceptor2};
//循环调用方法 1-2
for(int i =0;i<interceptors.length;i++){
    HandlerInterceptor obj =interceptors[i];
    obj.preHandle();
}

MyController.doSome();

// 2-1
for(int i =interceptors.length-1;i>=0;i--){
    HandlerInterceptor obj =interceptors[i];
    obj.postHandle();
}

简单实现验证功能

​ 这一部分在开发过程中逻辑大部分是相同的,可能发生改变的部分是判断账号的部分。

    /**
     * 验证功能
     *
     * */

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {

        System.out.println("MyInterceptor111拦截器的preHandle====");

//        获取登录的账号信息
        String username= "";
        Object attr = request.getSession().getAttribute("username");
        if(attr!=null){
            username = (String) attr;
        }

//        判断账号
        if("zhangsan".equals(username)){
            return true;
        }else{
            request.getRequestDispatcher("/tips.jsp").forward(request,response);
            return false;
        }
    }

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录</title>
</head>
<body>
    登录操作!!!
    <%
        session.setAttribute("username","zhangsan");
    %>
</body>
</html>
    
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>退出操作</title>
</head>
<body>
    退出系统!!!
    <%
        session.removeAttribute("username");
    %>
</body>
</html>    

3.3 拦截器和过滤器的对比

  1. 拦截器是springmvc框架中的对象。过滤器是servlet中的对象。

  2. 拦截器对象是框架容器创建的,过滤器对象是tomcat创建的对象。

  3. 拦截器是侧重对请求做判断,处理的,可以截断请求。过滤器是侧重对request,response对象设置值的。例如request.setCharacterEncoding(“utf-8”)

  4. 拦截器的执行时间有三个,控制器方法之前,之后,请求完成后。过滤器是在请求之前。

  5. 拦截器是拦截对controller,动态资源请求的。过滤器可以过滤所有请求动态的和静态的。

  6. 拦截器和过滤器一起执行的,先执行的过滤器,后面是*调度器,后面才是拦截器,再后面是控制器方法。

上一篇:电脑如何设置定时关机?


下一篇:java并发编程(十二)——如何正确关闭线程池?