一、访问静态资源问题
1、*.do
在没有特殊要求的情况下,SpringMVC 的中央调度器 DispatcherServlet 的
常使用后辍匹配方式,如写为*.do 或者 *.action, *.mvc 等。
2、 /
可以写为/,因为 DispatcherServlet 会将向静态资源的获取请求,例如.css、.js、.jpg、.png等资源的获取请求,当作是一个普通的 Controller 请求。中央调度器会调用处理器映射器为其查找相应的处理器。当然也是找不到的,所以在这种情况下,所有的静态资源获取请求也均会报 404 错误。
tomcat本身能处理静态资源的访问, 像html, 图片, js文件都是静态资源
tomcat的web.xml文件有一个servlet 名称是 default , 在服务器启动时创建的。
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern> 表示静态资源和未映射的请求都这个default处理
</servlet-mapping>
default这个servlet作用:
1.处理静态资源
2.处理未映射到其它servlet的请求。
所以我们在webxml文件中配置url-pattern的值为“/”会拦截所有的静态资源。包括jsp,html,js等但是我们的动态资源就是控制器类是可以访问的
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--自定义springmvc读取的配置文件的位置-->
<init-param>
<param-name>contextConfigLocation</param-name>
<!--指定自定义文件的位置-->
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--在tomcat启动就会创建Servlet对象-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!--
使用框架的时候,url-pattern可以使用两种值
1.使用扩展名方式,语法: *.xxxx,xxxx是自定义的扩展名,常用方式 *.do | *.action | *.mvc等
2.使用斜杠"/"
当项目中使用了 / 他会替代tomcat中的default
-->
<url-pattern>/</url-pattern>
</servlet-mapping>
3、方式一:使用<mvc:default-servlet-handler/>
因为我们在web.xml中设置了"/"。所以我们静态图片,资源都是无法访问的。
所以我们在application.xml中去设置该注解
-
声明 了
<mvc:default-servlet-handler />
后 , springmvc 框 架 会 在 容 器 中 创 建
DefaultServletHttpRequestHandler 处理器对象。 -
它会像一个检查员,对进入 DispatcherServlet的 URL 进行筛查,如果发现是静态资源的请求,就将该请求转由 Web 应用服务器默认的Servlet 处理。
-
一般的服务器都有默认的 Servlet。在 Tomcat 中,有一个专门用于处理静态资源访问的 Servlet 名叫 DefaultServlet。其
<servlet-name/>
为 default。可以处理各种静态资源访问请求。该 Servlet 注册在 Tomcat 服务器的 web.xml 中。在 Tomcat 安装目录/conf/web.xml。
处理方式:只需要在 springmvc.xml 中添加<mvc:default-servlet-handler/>
标签即可
<mvc:default-servlet-handler/>
表示使用 DefaultServletHttpRequestHandler 处理器对象。
而该处理器调用了 Tomcat 的 DefaultServlet 来处理静态资源的访问请求。
4、方式二:使用<mvc:resources/>
在 Spring3.0 版本后,Spring 定义了专门用于处理静态资源访问请求的处理器
ResourceHttpRequestHandler。并且添加了<mvc:resources/>
标签,专门用于解决静态资源无法访问问题。需要在 springmvc 配置文件中添加如下形式的配置:
<mvc:resources mapping="/images/" location="/images/**"/>
-
location 表示静态资源所在目录。当然,目录不要使用/WEB-INF/及其子目录。
-
mapping 表 示 对 该 资 源 的 请 求 ( 以 /images/ 开 始 的 请 求 , 如 /image/beauty.jpg ,/images/car.png 等)。注意,后面是两个星号**。
常见开发中,会进行如下配置:
<mvc:resources mapping="/static/" location="/static/**"/>
5. 方式三:声明注解驱动
解决动态资源和静态资源冲突的问题,在 springmvc 配置文件加入:
<!--加入注解驱动-->
<mvc:annotation-driven/>
二、请求重定向与转发
1.请求转发
处理器方法返回 ModelAndView 时,需在 setViewName()指定的视图前添加 forward:
,且此时的视图不再与视图解析器一同工作,这样可以在配置了解析器时指定不同位置的视图
视图页面必须写出相对于项目根的路径。forward 操作不需要视图解析器。
处理器方法返回 String,在视图路径前面加入 forward:
视图完整路径
@RequestMapping( value = "/doForward.do" )
public ModelAndView doForward(){
ModelAndView modelAndView = new ModelAndView();
//显示转发,明显的说明 这是一个转发
//modelAndView.setViewName("forward:/WEB-INF/show.jsp");
modelAndView.setViewName("forward:/hello.jsp");
return modelAndView;
}
注意此处,我们如果想要脱离视图解析器,可以使用该方法,跳出规则限制。想转发到哪个页面就哪个页面, 也称为显示转发,明显的说明这是一个转发
2.请求重定向
在处理器方法返回的视图字符串的前面添加 redirect:
,则可实现重定向跳转。
/*
* 处理器方法返回ModelAndView,实现转发forword
* 语法:setViewName("redirect:视图文件完整路径")
* redirect特点:不和视图解析器一同使用,就当项目中没有视图解析器
*
* */
@RequestMapping( value = "/doRedirect.do" )
public ModelAndView doRedirect(){
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("redirect:/hello.jsp");
return modelAndView;
}
此时我们通过表单请求去重定向页面
<h1>返回ModelAndView实现redirect</h1>
<form action="doRedirect.do" method="post">
姓名:<input type="text" name="name"><br/>
年龄:<input type="text" name="age"><br/>
<input type="submit" value="提交参数">
</form>
此时我们并不能用EL从中获取到数据,而是变成了GET请求方式
注意:
- 框架对重定向的操作是框架会把Model的简单类型的数据,转为String使用,作为show.jsp的get请求参数使用。目的是在doRedirect.do和show.jsp两次请求之间传递数据
- 该处不能进行直接访问,因为我们重定向不能访问WEB-INF下面的路径文件
如果我们想要获取该数据,我们可以使用param可以获取到域中的参数
body>
<h1>WEB-INF/view/show.jsp</h1>
<h3>myname数据:${param.myname}</h3>
<h3>myage数据:${param.myage}</h3>
</body>
三、异常处理
-
SpringMVC框架采用的是统一,全局的异常处理。把Controller中的所有异常处理都几种到一个地方。采用aop的思想。把业务逻辑和异常处理代码分开,解耦合。
-
使用两个注解
@ExceptionHandler
和@ControllerAdvice
1.异常处理步骤:
-
1.新建maven web项目
-
2.加入依赖
-
3.新建一个自定义异常类 MyUserException , 再定义它的子类NameException ,AgeException
-
4.在controller抛出NameException , AgeException
-
5.创建一个普通类,作用全局异常处理类
- 在类的上面加入@ControllerAdvice
- 在类中定义方法,方法的上面加入@ExceptionHandler
-
6.创建处理异常的视图页面
-
7.创建springmvc的配置文件
- 1)组件扫描器 ,扫描@Controller注解
- 2)组件扫描器,扫描@ControllerAdvice所在的包名
- 3)声明注解驱动
2.代码实现
书写一个index页面
<h1>全局异常处理</h1>
<form action="some.do" method="post">
姓名:<input type="text" name="name"><br/>
年龄:<input type="text" name="age"><br/>
<input type="submit" value="提交参数">
</form>
书写三个异常类,用于处理姓名和年龄异常
//用户异常
public class MyUserExpection extends Exception{
public MyUserExpection() {
super();
}
public MyUserExpection(String message) {
super(message);
}
}
//姓名异常
public class NameExpection extends MyUserExpection {
public NameExpection() {
super();
}
public NameExpection(String message) {
super(message);
}
}
//年龄异常
public class AgeExpection extends MyUserExpection {
public AgeExpection() {
super();
}
public AgeExpection(String message) {
super(message);
}
}
控制器类
@RequestMapping( value = "/some.do" )
public ModelAndView doSome(String name,Integer age) throws MyUserExpection {
ModelAndView modelAndView = new ModelAndView();
if (!"zs".equals(name)) {
throw new NameExpection("姓名不正确");
}
if (age == null || age >80) {
throw new AgeExpection("年龄较大");
}
modelAndView.addObject("myname",name);
modelAndView.addObject("myage",age);
modelAndView.setViewName("show");
return modelAndView;
}
xml中配置注解扫描器
<!--处理异常-->
<context:component-scan base-package="com.aiit.hander"/>
<mvc:annotation-driven/>
总异常处理类
异常发生处理逻辑
1.需要把异常记录下来,记录到数据库,日志文件
记录日志发生的时间,哪个方法发生的,异常错误内容
2.发送通知,把异常的信息通过邮箱,短信,微信发送给相关人员
3.给用户有友好的提示
注意使用两个注解即可@ControllerAdvice
和@ExceptionHandler
/*
* @ControllerAdvice 控制器增强(给控制器去增加功能——异常处理功能)
* 位置:在类的上面
* 特点:必须让框架知道这个注解所在的包名,需要在springmvc配置文件声明组件扫描器
* 指定@ControllerAdvice所在的包名
* */
@ControllerAdvice
public class GlobalExceptionHandler {
//定义方法,处理发生的异常
/*
* 处理异常的方法和控制器方法的定义一样,可以有多个参数,可以有ModelAndView,
* String ,void ,对象类型的返回值
*
* 形参:Expection。表示Controller中抛出的异常对象
* 通过形参可以获取发生的异常信息
*
* @ExceptionHandler(异常Class):表示异常的类型,当发生此类型异常时,
* 由当前方法处理
* */
@ExceptionHandler( value = NameExpection.class)
public ModelAndView doNameException(Exception exe){
//处理NameException的异常
/*
* 异常发生处理逻辑
* 1.需要把异常记录下来,记录到数据库,日志文件
* 记录日志发生的时间,哪个方法发生的,异常错误内容
* 2.发送通知,把异常的信息通过邮箱,短信,微信发送给相关人员
* 3.给用户有友好的提示
* */
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg","姓名必须是zs,其他用户不能访问");
modelAndView.addObject("exe",exe);
modelAndView.setViewName("nameError");
return modelAndView;
}
@ExceptionHandler( value = AgeExpection.class)
public ModelAndView doAgeException(Exception exe){
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg","年龄不能过大");
modelAndView.addObject("exe",exe);
modelAndView.setViewName("ageError");
return modelAndView;
}
//处理其他异常,NameException,AgeException以外,不知的类型异常
@ExceptionHandler
public ModelAndView doOtherException(Exception exe){
//处理其他异常
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg","年龄不能过大");
modelAndView.addObject("exe",exe);
modelAndView.setViewName("defaultError");
return modelAndView;
}
}
四、拦截器
1.拦截器概述
-
它的主要作用是拦截指定的用户请求,并进行相应的预处理与后处理。
-
拦截器是springmvc中的一种,需要实现HandlerInterceptor接口。
-
过滤器是用来过滤器请求参数,设置编码字符集等工作
-
拦截器是拦截用户的请求,做请求做判断处理的。
-
拦截器是全局的,可以对多个Controller做拦截。
一个项目中可以有0个或多个拦截器, 他们在一起拦截用户的请求。
拦截器常用在:用户登录处理,权限检查, 记录日志。
2.拦截器使用步骤
-
定义类实现HandlerInterceptor接口
-
在springmvc配置文件中,声明拦截器, 让框架知道拦截器的存在
3.创建一个拦截器
//拦截器类:拦截用户的请求。
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("拦截器的MyInterceptor的preHandle()");
//计算的业务逻辑,根据计算结果,返回true或者false
//给浏览器一个返回结果
return true;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler, ModelAndView mv) throws Exception {
System.out.println("拦截器的MyInterceptor的postHandle()");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
System.out.println("拦截器的MyInterceptor的afterCompletion()");
}
}
方法简介
1、preHandle:预处理方法
-
是整个项目的入口,门户。 当preHandle返回true 请求可以被处理。
-
参数:
- Object handler : 被拦截的控制器对象
- 返回值boolean
1. true:请求是通过了拦截器的验证,可以执行处理器方法。 拦截器的MyInterceptor的preHandle() =====执行MyController中的doSome方法===== 拦截器的MyInterceptor的postHandle() 拦截器的MyInterceptor的afterCompletion() 2. false:请求没有通过拦截器的验证,请求到达拦截器就截止了。 请求没有被处理 拦截器的MyInterceptor的preHandle()
-
特点:
1.方法在控制器方法(MyController的doSome)之前先执行的。 用户的请求首先到达此方法 2.在这个 方法中可以获取请求的信息, 验证请求是否符合要求。 可以验证用户是否登录, 验证用户是否有权限访问某个连接地址(url)。 如果验证失败,可以截断请求,请求不能被处理。 如果验证成功,可以放行请求,此时控制器方法才能执行。
2、postHandle:后处理方法。
-
参数:
Object handler:被拦截的处理器对象MyController
ModelAndView mv:处理器方法的返回值 -
特点:
1.在处理器方法之后执行的(MyController.doSome()) 2.能够获取到处理器方法的返回值ModelAndView,可以修改ModelAndView中的 数据和视图,可以影响到最后的执行结果。 3.主要是对原来的执行结果做二次修正, ModelAndView mv = MyController.doSome(); postHandle(request,response,handler,mv);
3、afterCompletion:最后执行的方法
-
参数:
Object handler:被拦截器的处理器对象
Exception ex:程序中发生的异常 -
特点:
1.在请求处理完成后执行的。框架中规定是当你的视图处理完成后,对视图执行了forward。就认为请求处理完成。 2.一般做资源回收工作的, 程序请求过程中创建了一些对象,在这里可以删除,把占用的内存回收。
4.拦截器执行顺序
5.执行多个拦截器
我们只需要多写一个拦截器类即可。并且在Spring.xml中声明多个拦截器即可
<mvc:interceptors>
<!--声明第一个拦截器-->
<mvc:interceptor>
<!--用来指定拦截的请求uri地址
path:就是uri地址,可以使用通配符
**表示任意字符,文件或者多级目录和目录中的文件
如/user/**
/**表示拦截所有
-->
<mvc:mapping path="/**"/>
<!--声明拦截器对象-->
<bean class="com.aiit.handler.MyInterceptor"/>
</mvc:interceptor>
<!--声明第二个拦截器-->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.aiit.handler.MyInterceptor2"/>
</mvc:interceptor>
</mvc:interceptors>
注意:
- 声明拦截器,拦截器可以有0或多个
- 在框架中保存多个拦截器是ArrayList。
- 按照声明的先后顺序放入到ArrayList中
输出结果:
类似于栈的执行顺序,先进后出的概念
6.拦截器的执行链
两个拦截器中
第一个preHandle返回值为true,第二个preHandle返回值为false
第一个preHandle返回值为false,第二个preHandle返回值为true
结论:不论是哪个拦截器,只要有一个拦截器的preHandle的一个返回值为false那么均不会执行控制器中的方法,我们只要在不同的拦截器中进行不同的功能即可,均正确才能返回我们的控制器类
五、过滤器和拦截器的区别
-
过滤器是servlet中的对象, 拦截器是框架中的对象
-
过滤器实现Filter接口的对象, 拦截器是实现HandlerInterceptor
-
过滤器是用来设置request,response的参数,属性的,侧重对数据过滤的。
-
拦截器是用来验证请求的,能截断请求。
-
过滤器是在拦截器之前先执行的
-
过滤器是tomcat服务器创建的对象
-
拦截器是springmvc容器中创建的对象
-
过滤器是一个执行时间点
-
拦截器有三个执行时间点
-
过滤器可以处理jsp,js,html等等
-
拦截器是侧重拦截对Controller的对象。 如果你的请求不能被DispatcherServlet接收, 这个请求不会执行拦截器内容
-
拦截器拦截普通类方法执行,过滤器过滤servlet请求响应