springMVC
内容管理
javaWeb—SSM中最后控制层MVC框架
异常处理和拦截器
SSM整合其实只是spring和mybatis的整合,springmvc是spring的子结构,所以spirng的配置文件对于mvc来说是可见的;forward和redirct显式路径可以在配置视图解析器的情况下寻找其他的位置的资源
开发最开始就是编写好框架结构:创建好配置文件,加入要使用到的依赖,创建包的结构;业务都是在结构建立好之后才开始编写;
集中处理异常
之前没有使用框架之前,要想处理异常只能分开处理,也就是说每一个方法都需要单独处理异常,比如在A方法中try----catch;在B方法中也try----catch,加上try–catch就代码就很凌乱;MVC利用AOP的思想在一个固定的位置统一处理异常,这样异常发生变动就不用大段修改,并且代码就不需要大量的try catch;代码的结构就更清晰
因为这里的try catch实际上也是属于交叉业务逻辑的,所以将异常使用AOP的思想剥离出来
SpringMVC框架采用的是统一、全局的异常处理,把controller中的所有的异常处理都几种到一个地方,使用的aop的思想,把业务逻辑和异常处理代码分开,解耦合,主要依赖的是两个注解: @ExceptionOnHandler; @ControllerAdvice
这里就演示一下mvc框架使用之后新的异常的处理方式
创建页面、控制器方法,异常类
这里就基于之前建立的项目的基础上来进行操作; 这里建立两个异常类
</body> <br><br>
方法返回值为modelAndView的时候实现请求转发
<form action="test/some.do" method="post">
用户名<input type="text" name="user"/><br>
年龄<input type="text" name="age"/><br>
<input type="submit" value="注册"/>
</form>
自定义一个异常类UserException,定义两个子类NameException和AgeException
集中统一处理异常和控制器的创建类似
package Jning.exception;
/**
* 当年龄有问题的时候抛出的异常;
*/
public class AgeException extends UserException {
public AgeException() {
super();
}
public AgeException(String message) {
super(message);
}
}
//这里就简单定义了两个异常类
最后简单定义控制器方法,让出现异常的时候抛出,比如姓名不是张三,年龄大于80都会抛出异常
@RequestMapping(value ="/some.do",produces = "text/html;charset=utf-8")
public ModelAndView doSome(String user,int age) throws UserException { // 抛出父类的异常便于统一处理,不再需要try catch
ModelAndView mv = new ModelAndView();
if(!"张三".equals(user)) {
throw new NameException("非法的用户,拒绝访问");
}
if(age > 30) {
throw new AgeException("不是你这个年龄段该访问的");
}
mv.addObject("user",user);
mv.addObject("age",age);
mv.setViewName("forward:/static/html/test.jsp");
return mv;
}
如果按照之前的做法,那么就不会在方法的上面进行异常的抛出,而是直接使用try catch环绕,自己处理,但是加入异常之后很凌乱;并且按照之前的操作,如果直接像这里直接抛出,那么调用者方法就要处理,如果一直都不处理,那么就是JVM处理,直接终止程序;
使用MVC框架就不需要再在某个调用的方法中来处理异常了,而是直接在框架中进行处理
定义一个集中处理异常类,类上面加上@ControllerAdvice;
这里相当于也是一个创建对象,只是不是创建控制层或者其他的对象,而是创建一个advice对象;所以要需要扫描这个包Exception; 同时要识别类中的注解,还需要注解驱动,之前加过,不需要加,所以只是需要在MVC的配置文件中额外加上一个扫描器
类方法上加上@ExceptionHandler
定义的方法就是处理某些异常的
package Jning.handler;
import Jning.exception.AgeException;
import Jning.exception.NameException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
/**
* 使用框架来集中处理异常
* ControllerAdvice 控制器通知;advice也称为增强,所以也就是控制器增强;
* 使用aop增加异常处理功能【controller为最终的调用者,service等抛出的异常都需要处理
* 所以mvc框架中就会专门对控制器进行异常的处理
* ControllerAdvice和之前的控制器类似,方法的定义和控制器方法相同
*/
@ControllerAdvice
public class GlobalExceptionHandler {
//定义方法,处理发生的异常;这个方法的定义和之前的处理器方法的定义相同,比如ModelAndView、String等
/**
* 形参:Exception,表示的是控制器方法抛出的异常对象,通过形参可以获取异常的信息
* @ExceptionHandler 属性value的值为class;表示的是异常类的类型,当发生这种类型的异常的时候,就是
* 注解的方法来处理异常
*/
@ExceptionHandler(value = NameException.class)
public ModelAndView doNameException(Exception e) {
/**
* 异常发生处理器,处理控制层中所有的该类异常,开发中一般记录异常,将异常
* 记录到数据库,日志文件;记录发生的时间、那个方法发生、异常的错误内容
* 同时要发送通知,将异常的信息通过邮件等通知开发人员,同时要给用户发送请求
*/
//这里就简单给用户进行提示
ModelAndView mv = new ModelAndView();
mv.addObject("msg","姓名必须是张三,其他用户拒绝访问");
mv.addObject("error",e);
mv.setViewName("forward:/static/html/error.jsp");
return mv;
//发生异常之前的页面
}
//处理AagException
@ExceptionHandler(value = AgeException.class)
public ModelAndView doAgeException(Exception e) {
ModelAndView mv = new ModelAndView();
mv.addObject("msg","你好,你的年龄不能访问这个网页");
mv.addObject("error",e);
mv.setViewName("forward:/static/html/error.jsp");
return mv;
}
//捕获其他的未知的异常,因为不一定会发现,不知名的异常
@ExceptionHandler
public ModelAndView doOtherException(Exception e) {
ModelAndView mv = new ModelAndView();
mv.addObject("msg","发生了未知的异常");
mv.addObject("error",e);
mv.setViewName("forward:/static/html/error.jsp");
return mv;
}
}
当控制层抛出异常的时候,会自动跳转到这个异常处理器中,会将异常的类型和处理器方法进行比较,当比较成功的时候就执行对应的处理方法,如果处理失败,就会执行默认的,类似于if else中的default中的操作; 只能由一个没有value属性的异常处理器方法代表其他的未知的异常的处理方法
创建异常的页面
这里就简单创建一个异常处理的页面,发生异常的时候会处理器方法,从而跳转错误页面,而不会去执行控制层方法中的页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Error</title>
</head>
<body>
<h1 align="center">出错啦</h1>
<hr color="pink">
<font color="#a52a2a">对不起, ${msg}<br>错误的信息: ${error.message}</font>
</body>
</html>
执行的过程就是:
用户发送请求跳转到处理器方法中执行,如果处理器方法抛出异常,【系统发现由集中处理异常的类,@ControllerAdvise],然后就会停止执行控制器方法,而是将请求转发到异常处理的类
从上向下依次寻找和抛出的异常类的类型相同的@ExceptionHandler的value
;进入执行第一个找到的方法、所以就算同时发生了很多异常,还是只是会执行第一个匹配到的方法;然后正常给用户反馈;都没有匹配上就会进入执行未知的异常的错误信息
其实这里ConrollerAdivice就是使用的AOP对Controller进行增强,相当于是动态代理,所以发生异常执行的方法其实还是一个控制器,能够处理请求和发送请求给结果页面
这里可以看一下结果
------------张三 25------------------
因为不是JSP页面,并且不在WEB-INF下
访问的用户:张三
年龄:25
----------------张古 25---------------------------
因为不是JSP页面,并且不在WEB-INF下
访问的用户:张三
年龄:25
-------------------张三 39---------------
对不起, 你好,你的年龄不能访问这个网页
错误的信息: 不是你这个年龄段该访问的
------------张三 null-----------------------
对不起, 发生了未知的异常
错误的信息: Failed to convert value of type 'java.lang.String' to required type 'int'; nested exception is java.lang.NumberFormatException: For input string: ""
第三个就是因为接收的参数是空,然后框架内部进行自动类型转换,Integer.ValueOf();null不能转为int类型,这里就抛出了异常【发生了异常】
所以其实就是控制层方法发生异常的时候就会自动执行集中统一处理异常类的方法,这个类的定义和控制器差不多,使用的类的注解是ControllerAdvice;方法上的注解是@ExceptionHandler,属性value代表的是Exception的类型,最后会放一个没有value的代表的是default,发生的其他的没有识别的异常的时候的处理【控制台就不会报错了,因为会继续执行,不会因为异常而不能执行接下来的操作】不再需要在controller中使用try–catch处理异常,集中处理所有的控制层的异常;AOP
其实如果不想过多处理异常,直接定义一个default异常处理方法即可
项目中除了之前的dao、service、controller、entity包之外,多了exception来定义自定义的异常类;handler【处理器】包中存放的就是对于控制层/用户请求的处理器,比如全局异常集中处理器,拦截处理器等
拦截器 实现HandlerInterceptor
MVC中的interceptor拦截器是非常重要的,主要作用是拦截指定的用户请求,并进行相关的预处理和后处理,拦截的时间点 : 处理器映射器根据用户所提交的请求映射出索要执行的处理器类,找到该处理器类的处理适配器,在处理器适配器执行处理器之前; 处理器映射出所要执行处理器类时,已经将拦截器和处理器组合为一个处理器执行链,并交给了中央调度器
这里使用的思想还是AOP
-
拦截器和过滤器类似,但是功能方向侧重不同 : 过滤器是过滤请求参数的,设置编码字符集等, 拦截器拦截请求,做请求得判断处理【在原生servlet中,应该都是filter来处理请求】
-
拦截器是全局的,可以对多个controller进行拦截,一个项目可以有0-多个拦截器,一起拦截用户的请求;拦截器经常用在 : 用户的登录处理,权限的检查,记录日志等……
-
拦截器拦截的时间 : 一共有3个时间
- 请求处理之前,也就是controller类的方法执行之前被拦截
- 在控制器方法执行后也会拦截
- 在请求处理完成后也会执行拦截器
-
拦截器中的页面路径不能使用视图解析器,视图解析器只是为控制器工作【异常集中处理只是控制增强,是同一个类型】,拦截器在控制器方法之前的prehadnle不能使用
使用拦截器很简单 : 首先就是声明拦截器 :创建一个类实现HanlerInterceptor接口 ; 在Spirng配置文件中声明拦截器;让springMVC知道拦截器的存在
声明一个拦截器
这里就还是使用之前的异常处理的类,这里就不抛出异常了; 创建一个普通的类,实现HandlerInterceptor接口; 接口中有3个方法都要实现
接口中的3个方法分别代表的是不同的执行时机;preHandle代表的是在处理器方法执行之前进行拦截;postHandle代表的是在处理器方法执行后进行拦截【这个时候执行执行完了方法,中央调度器还没有将mv进行请求转发】;afterCompletion是在请求处理完成之后执行,也就是放回的结果页面的请求进行了forward之后就会进行拦截
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}
default类型的,不用全部都实现,可以选择性实现【依据功能】
-
preHandle
: @param handler Object类型,表示被拦截的控制器对象【controller】 ;是整个项目的门户;就是之前的原生servlet的filter来处理,登录验证等- 这个方法是在控制器方法执行之前先执行的,也就是controller中的do方法,用户的请求先到达这里
- 返回值为bool类型,true就和之前的过滤器放行一样,true就是不执行控制器方法,
被拦截后可以通过request重定向到其他的页面给用户进行反馈
;表明请求被拦截 - 在这个方法中可以获取请求的信息,验证请求是否符合请求,可以验证用户是否登录,是否有权限访问, 如果验证成功,放行请求,请求到达处理器对象进行处理;如果验证失败,请求不能进行处理
-
postHandle
: 参数也有控制器对象- 后处理方法,是在控制器方法执行之后才会触发执行验证
- 所以可以获取处理器方法的返回值modelAndView对象,可以修改这个对象的数据和视图,主要是对于原来的结果进行二次修正
-
afterCompletion
: Exception: 表示程序所发生的异常- 最后执行的方法,在请求处理完成后执行的,框架中规定的是当视图处理完成后,对视图进行了forward,那么就认为请求处理完成
- 一般是用作资源的会后工作的,程序执行过程中创建的对象,在这里可以进行删除,把占用的内存回收
执行的时间 : 首先是prehandle ----> 控制层处理器方法doSome —> postHandle —> afterCompletion
mvc配置文件声明拦截器,指定url, mvc:interceptors
在mvc配置文件中声明拦截器,这里可以声明一个或者多个拦截器,使用的标签是interceptors;这里代表的就是多个拦截器,其中可以声明单个的拦截器interceptor
单个拦截器中,mapping表示就是拦截的url,path指定路径,可以通配
<!-- 声明拦截器,拦截器可以有多个 -->
<mvc:interceptors>
<!-- 声明一个拦截器 -->
<mvc:interceptor>
<!-- 指定拦截器的拦截的url得知,可以通配,**代表任意字符 -->
<mvc:mapping path="/test/**"/>
<!-- 声明对象; 其实就是和在web.xml中配置过滤器相同,要指明class和url -->
<bean class="Jning.handler.MyInterceptorHandler"/>
</mvc:interceptor>
</mvc:interceptors>
其实一个拦截器就和之前servlet一个过滤器类似,需要指定class类型和拦截的url
第一个方法:prehandle
这个方法是最经常使用的,也就是在之前进行拦截,经常使用来进行验证等
package Jning.handler;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptorHandler implements HandlerInterceptor {
/**
* 三个方法分别代表拦截不同时间的触发时机
*preHandle表示的是预处理方法
* @param request 用户的请求
* @param response 响应对象
* @param handler Object类型,表示被拦截的控制器对象【controller】
*这个方法是在控制器方法执行之前先执行的,也就是controller中的do方法,用户的请求先到达这里
* 在这个方法中可以获取请求的信息,验证请求是否符合请求,可以验证用户是否登录,是否有权限访问,
* 如果验证成功,放行请求,请求到达处理器对象进行吃力
* 如果验证失败,请求不能进行处理
* @retrun : boolean类型; 真代表放行,假代表请求被拦截,没有到达控制器方法
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//现在使用拦截器完成刚刚的异常的处理,如果不是张三不允许访问
//需要进行其他的一些业务逻辑,操作判断
if(!"张三".equals(request.getParameter("user"))) {
//这里的参数是请求,只能手工取
//这里不能使用forward;因为这里还没有真正到达控制层,所以mvc的视图解析不能发挥作用
request.getRequestDispatcher("/static/html/error.jsp").forward(request,response);
return false;
}
return true;
}
}
----------------------------------------result----------------------------------
对不起,非法用户不允许访问,请求拦截了 对不起,
错误的信息:
可以看到,这样就可以对于用户的请求进行拦截,通过之后才会放行,需要注意路径没有开启视图解析,要按照之前的普通的路径写法
第二个方法: postHandle
这个方法主要用于修正视图的
package Jning.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;
public class MyInterceptorHandler implements HandlerInterceptor {
/**
* @param handler
* @param modelAndView
* 后处理方法,是在控制器方法执行之后才会触发执行验证
* 所以可以获取处理器方法的返回值modelAndView对象,可以修改这个对象的数据和视图
* 主要是对于原来的结果进行二次修正
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("执行拦截器的postHandle方法");
if(modelAndView != null) {
//说明处理器方法是返回了视图和数据,那么使用postHandle方法修改数据
modelAndView.addObject("date",new Date().getTime());
}
}
}
--------------------------------------result----------------------------------
访问的用户:张三
年龄:25
使用拦截器修正后的数据: 日期 : 1642418618754
最常用的还是第一个pre方法,这个修正的方法主要是后期如果突然想修改一下数据的时候就使用这种方式进行拦截
第三个方法:afterCompletion
这个方法是在请求处理完成之后才会执行,也就是当处理器方法执行完,框架将请求forward到结果页面的时候就是这个方法执行的时机。一般用于资源的释放【代表一次请求的完成】
这里简单使用以下,计算一次请求得耗时;在第一个preHandle方法计算开始时间,该方法计算结束时间,相差即为执行时间
package Jning.handler;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptorHandler implements HandlerInterceptor {
private long bTime;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//现在使用拦截器完成刚刚的异常的处理,如果不是张三不允许访问
System.out.println("执行了拦截器preHandle方法");
bTime = System.currentTimeMillis();
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("执行拦截器的postHandle方法");
}
/**
* afterCompletion : 最后执行的方法
* Exception: 表示程序所发生的异常
*在请求处理完成后执行的,框架中规定的是当视图处理完成后,对视图进行了forward,那么就认为请求处理完成
* 一般是用作资源的会后工作的,程序执行过程中创建的对象,在这里可以进行删除,把占用的内存回收
*/
@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("该次请求执行时间是 :" + (eTime - bTime) + " ms");
}
}
可以看看结果
执行了拦截器preHandle方法
执行了controller得doSome方法
执行拦截器的postHandle方法
执行了拦截器得afterCompletion方法
该次请求执行时间是 :780 ms
执行了拦截器preHandle方法
执行了controller得doSome方法
执行拦截器的postHandle方法
执行了拦截器得afterCompletion方法
该次请求执行时间是 :2 ms
第一次请求因为需要浏览器加载界面,解析资源,所以耗时较长,第二次时间骤降到2ms;这里还可以看到执行的顺序就是prehandle—> doSome—> postHandle—>afterCompletion
多个拦截器的执行顺序 【拦截器
拦截器在mvc中,框架中是使用的ArrayList的,按照声明的顺序依次放入ArrayList集合,所以如果在路径相同的情况下,先声明的先执行
这可以声明再声明一个和上面一模一样的拦截器,这个新的拦截器为2
package Jning.handler;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptorHandler2 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//现在使用拦截器完成刚刚的异常的处理,如果不是张三不允许访问
System.out.println("执行了拦截器2preHandle方法");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("执行拦截器2的postHandle方法");
}
/**
* afterCompletion : 最后执行的方法
* Exception: 表示程序所发生的异常
*在请求处理完成后执行的,框架中规定的是当视图处理完成后,对视图进行了forward,那么就认为请求处理完成
* 一般是用作资源的会后工作的,程序执行过程中创建的对象,在这里可以进行删除,把占用的内存回收
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("执行了拦截器2得afterCompletion方法");
}
}
这里注意一定要将拦截器注册,不然mvc不能识别
<mvc:interceptors>
<!-- 声明一个拦截器 -->
<mvc:interceptor>
<!-- 指定拦截器的拦截的url得知,可以通配,**代表任意字符 -->
<mvc:mapping path="/test/**"/>
<!-- 声明对象; 其实就是和在web.xml中配置过滤器相同,要指明class和url -->
<bean class="Jning.handler.MyInterceptorHandler"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/test/**"/>
<bean class="Jning.handler.MyInterceptorHandler2"/>
</mvc:interceptor>
</mvc:interceptors>
然后运行程序查看运行的结果
执行了拦截器preHandle方法
执行了拦截器2preHandle方法
执行了controller得doSome方法
执行拦截器2的postHandle方法
执行拦截器的postHandle方法
执行了拦截器2得afterCompletion方法
执行了拦截器得afterCompletion方法
该次请求执行时间是 :876 ms
也就是先执行了最先放入的拦截器的pre方法,然后执行拦截器2的pre方法;之后执行doSome方法,后面先执行2的post方法再1,comp方法也是; 其实想象为ArrayList,就是从前到后,从后向前的过程
其实拦截器就是一个盒子模型,最中间的就是控制层,一个拦截器就是一层盒子,最先声明的拦截器在最外面,所以进的时候就是先1后2,出的时候就是先2后1
其余的情况就是按照路径的优先级进行匹配
拦截器链就是一个物理链路,也就是上面的盒子的模型,进不去就要出去;如果连第一层都不放行就更加不会执行的【串联】
实际开发中,不同的拦截器执行的不同的功能,要所有的拦截器都验证通过,才会放行
拦截器、过滤器的区别
- 过滤器是servlet规范中的对象,拦截器是框架中的对象【所以过滤器范围更大】
- 过滤器是实现的filter接口,拦截器是实现的HandlerInterceptor接口
- 过滤器是用来过滤参数的,也就是用来设置request、response的参数、属性;侧重数据的过滤;而拦截器是用来验证请求得,侧重截断请求
- 过滤器是在拦截器之前先执行的; 过滤器是tomcat服务器创建的对象,拦截器是springmvc容器的对象,先访问的是tomcat
- 过滤器是一个执行时间点,就最开始的时候;而拦截器是多个
- 过滤器可以处理各种资源jsp、js、html;拦截器是侧重对于controller的对象,如果请求不能被Dispatcher接收,那么就不会执行拦截器的内容【拦截器是dispatcher来管理的,在其之后,而中央调度器是servlet;filter是在servlet之前,所以filter还在前面 -->上面的图说明】过滤器就是拦截普通的方法的,但是过滤器是过滤的servlet;控制器方法不是servlet;只是在代替其的功能
拦截器实例: 用户权限
使用拦截器来检查登录的用户是否能访问系统
这里就使用上面的项目的结构,就是用户输入姓名和年龄;使用controller来接收请求;结果页面就是result.jsp;
模拟用户的登录【创建一个login.jsp ,将用户的信息放入session】,创建一个logout.jsp,模拟退出系统【将用户的信息从session中删除】;同时还要给一个错误界面,如果失败了就给一个提示
</body> <br><br>
方法返回值为modelAndView的时候实现请求转发
<form action="test/some.do" method="post">
用户名<input type="text" name="user"/><br>
年龄<input type="text" name="age"/><br>
<input type="submit" value="注册"/>
</form>
再来4个jsp页面
- login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录系统</title>
</head>
<body>
模拟登录,用户登录系统
<%
session.setAttribute("userName","张三");
session.setAttribute("age",request.getParameter("age"));
%>
</body>
</html>
- logout.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>logOut</title>
</head>
<body>
模拟用户登出系统
<%
session.removeAttribute("userName");
session.removeAttribute("age");
%>
</body>
</html>
- result.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<h1>result</h1>
<hr color="pink">
<hr color="pink">
<font color="#7fffd4">你好,成功登录系统, ${user}</font>
</body>
</html>
- error.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Error</title>
</head>
<body>
<h1 align="center">出错啦</h1>
<hr color="pink">
对不起,非法用户不允许访问,请求拦截了
<font color="#a52a2a">对不起, ${msg}<br>错误的信息: ${error.message}</font>
</body>
</html>
其实就是刚刚演示的那个;用户模拟登录login.jsp之后,服务端就有session;这样才能成功访问
//拦截器
package Jning.handler;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptorHandler implements HandlerInterceptor {
private long bTime;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
bTime = System.currentTimeMillis();
//验证登录的用户信息,获取用户的session
String logName = "";
Object attr = request.getSession().getAttribute("userName");
if(attr != null) {
logName = (String) attr;
}
//判断登录的账户是否符合要求
if(!"张三".equals(logName)) {
//不能访问系统,给用户提示
request.getRequestDispatcher("/static/html/error.jsp").forward(request,response);
return false;
}
//是张三的账户
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
long eTime = System.currentTimeMillis();
System.out.println("该次请求执行时间是 :" + (eTime - bTime) + " ms");
}
}
//doSome方法
package Jning.controller;
import Jning.exception.AgeException;
import Jning.exception.NameException;
import Jning.exception.UserException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
@Controller
@RequestMapping(value ="/test")
public class RedirctTest {
@RequestMapping(value ="/some.do",produces = "text/html;charset=utf-8")
public ModelAndView doSome(String user,int age) { // 抛出父类的异常便于统一处理,不再需要try catch
System.out.println("执行了controller得doSome方法");
ModelAndView mv = new ModelAndView();
// if(!"张三".equals(user)) {
// throw new NameException("非法的用户,拒绝访问");
// }
// if(age > 30) {
// throw new AgeException("不是你这个年龄段该访问的");
// }
mv.addObject("user",user);
mv.addObject("age",age);
mv.setViewName("forward:/static/html/test.jsp");
return mv;
}
}
SpringMVC执行流程
springmvc的执行流程是一个流水线,其中发挥核心作用的就是DispactcherServlet
在框架中存在一些对象来帮助用户来调用容器创建的对象,在spring中要使用对象的时候就是通过容器来get即可,但是在spring中并没有这样子的操作
执行的流程上面也放了一张更完整的图:
- 用户发起请求XXXX
public interface HandlerMapping { //映射器,根据请求找到hanlder,也就是找到处理器controller方法
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
- DispatcherServlet接收请求,把请求转交给处理器映射器【处理器映射器: springmvc框架中的一种对象,该对象实现了HandlerMapping接口,和拦截器一样映射器一样有很多个】,根据请求,从springmvc容器对象中获取处理器对象contoller,框架找到的处理器对象被放到一个处理器执行链【HandlerExecutionChain】的类中进行执行【HandlerExcutionChain】 — 处理器执行链
public class HandlerExecutionChain {
private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
private final Object handler; -----处理器
private final List<HandlerInterceptor> interceptorList; ---->拦截器的数组
private int interceptorIndex;
项目中所有的拦截器对象还有处理器对象【controller对象】
- 中央调度器将2中的HandlerExecutionChainde 处理器对象交给处理器适配器对象【多个】,处理器适配器:springmvc中的对象,需要实现HandlerAdaptor接口; 这个对象的作用就是执行处理器的方法,调用处理器对象controller的do方法,得到返回值ModelAndView
public interface HandlerAdapter {
boolean supports(Object handler); ----> 就是执行处理方法
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
/** @deprecated */
@Deprecated
long getLastModified(HttpServletRequest request, Object handler);
}
- 中央调度器将3中获取的ModelAndView交给视图解析器对象;视图解析器:组成视图的完整视图,前缀、后缀,创建完整的View对象;View是一个接口,表示视图的,在框架中jsp、html不是string表示,而是使用View和子类来表示的
public interface View {
String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus";
String PATH_VARIABLES = View.class.getName() + ".pathVariables";
String SELECTED_CONTENT_TYPE = View.class.getName() + ".selectedContentType";
@Nullable
default String getContentType() {
return null;
}
void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
}
- 中央处理器将4中的View对象对象获取到,调用View的方法,把Model的数据放入到request作用域,执行对象视图的forward,请求结束
对于springMVC的简单介绍就是这些了,深入的部分之后会补充🎄