Spring MVC 介绍(三)

Source

Spring MVC 介绍(三)

Spring MVC 拦截器

  • HandlerInterceptor 接口

SpringMVC定义了拦截器接口,源代码如下:

public interface HandlerInterceptor {
	boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception;
	void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throws Exception;
	void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception;
}

该接口中定义了三个方法,这三个方法的调用时在SpringMVC框架内部完成的,调用这个三个方法的时候,其参数的值也是从框架内部传递进来的。

boolean preHandle

预处理方法,实现处理器方法的预处理,就是在处理器方法执行之前这个方法会被执行,相当于拦截了处理器方法,框架会传递请求和响应对象给该方法,第三个参数为被拦截的处理器方法。如果preHandle方法返回true表示继续流程(如调用下一个拦截器或处理器方法),返回false表示流程中断,不会继续调用其他的拦截器或处理器方法,此时我们需要通过response来产生响应;

void postHandle

后处理方法,实现处理器方法的后处理,就是在处理器方法调用完成,但在渲染视图之前,该方法被调用,此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理。

afterCompletion

整个请求处理完毕,即在视图渲染完毕时该方法被执行。

  • HandlerInterceptorAdapter 抽象类

SpringMVC定义了HandlerInterceptor接口,然后对该接口做了实现。

其中的HandlerInterceptorAdapter这个抽象类实现了HandlerInterceptor接口。preHandle 方法返回的值永远为true,postHandle和afterCompletion是空实现。我们需要添加自己的拦截器,只需要继承HandlerInterceptorAdapter即可,按照需要重写那三个方法即可。

  • 自定义拦截器实现步骤
  1. 编写一个类,继承HandlerInterceptorAdapter

  2. 注册拦截器

自定义的拦截器,SpringMVC并不知道,所以要将AllHandlerInterceptor这个自定义拦截器配置到SpringMVC框架的流程处理中,让它可以正常工作。

  1. 定义Controller类以及视图

总结一下SpringMVC 拦截器内部的工作流程:

① 系统启动初始化阶段,根据Spring配置文件的 mvc:interceptors 的配置,将interceptor对象配置到Spring容器中

② 客户端发出请求到DispatcherServlet,DispatcherServlet查看有没有拦截器与当前的请求相匹配,如果有则调用拦截器的preHandle方法进行预处理,这个预处理方法返回true,流程继续往下,否则流程结束。

③ 与处理方法返回true,HandlerAdapter适配到处理器方法后调用处理器方法。

④ 处理器方法执行完毕,经过HandlerMethodReturnValueHandler处理完毕后就可以获取到ModelAndView对象了,此时就调用拦截器的postHandle方法,并将request,response,被拦截的处理器方法对象,ModelAndView对象传递给postHandle方法

⑤ 视图解析器解析视图,并完成视图的渲染

⑥ 视图渲染完毕后调用拦截器的afterCompletion方法后,将完成了渲染的视图送到客户端后流程结束。

  • 拦截器链

如果系统中注册了多个拦截器,这多个拦截器就组成了拦截器链,这一点与 Servlet Filter一样。前面实现的拦截器拦截了所有的请求,现在再实现一个拦截器,只拦截部分请求,并与全局的拦截器组成一个拦截器链。

  • @ControllerAdvice与统一异常处理

除了HandlerInterceptor可以拦截处理器方法以外,SpringMVC中还可以使用AOP的方式来拦截处理器方法,即处理器方法的增强。@ControllerAdvice是Spring 3.2 中定义的annotation,它就可以用来增强/拦截处理器方法。

流程是:定义一个类,并使用@ControllerAdvice标注,指定要拦截的类或方法,然后在这个类中定义一些使用@ExceptionHandler,@ModelAttribute,@InitBinder标注的方法,当处理器方法被调用被拦截后,这些方法将会被调用。

不过@ModelAttribute,@InitBinder两个通常用处不大,@ExceptionHandler使用得最多,它方便用来做统一的异常处理。

视图解析器

SpringMVC 执行完处理器方法后,由HandlerMethodReturnValueHandler将请求的结果处理为ModelAndView对象,然后这个对象就交给视图解析器(ViewResolver)去处理。SpringMVC支持多种视图解析器,常用的有:

  • InternalResourceViewResolver : 前面章节所有的示例都使用的是这个解析器,它将视图名解析为一个URL文件地址,这个地址映射到WEB-INF目录下的jsp文件
  • JasperReportsViewResolver : jasperReports是一个基于Java的开源报表工具,该解析器将视图名解析为报表文件对应的URL
  • FreeMarkerViewResoler :这个解析器基于FreeMarker模板引擎技术,所有的视图渲染由FreeMarker模板引擎+FreeMarker模板最终生成HTML视图内容
  • VelocityViewResolver: 这个解析器基于Velocity模板引擎技术。
  • XmlViewResolver: 解析xml视图

也就是说利用SpringMVC开发Java Web应用的时候,JSP技术并不是唯一的选择,也可以基于其它模板引擎技术。开发的时候,我们可以选择一种视图解析器或者多种视图解析器混用,每个视图解析器都实现了Ordered接口,可以为视图解析器配置一个order属性用来指定解析器的优先顺序,order越小优先级越高。SpringMVC会按照视图解析器的优先顺序对逻辑视图名进行解析,直到解析成功并返回视图对象,否则抛出异常。

在SpringMVC配置文件中配置视图解析器,这里使用了InternalResourceViewResolver与VelocityViewResolver 混合使用的方式,为此而设置了两个视图解析器的优先级:

…
<beanclass="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="order" value="1"></property>
    <property name="viewClass"value="org.springframework.web.servlet.view.JstlView" /><property name="prefix" value="/WEB-INF/views/" />
    <property name="suffix" value=".jsp" />
    </bean><bean class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
        <property name="resourceLoaderPath" value="/WEB-INF/velocity/" />
        <property name="velocityProperties">
            <props>
                <prop key="input.encoding">UTF-8</prop>
                <prop key="output.encoding">UTF-8</prop>
            </props>
        </property>
</bean>
<beanclass="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
    <property name="order" value="0"></property>
    <property name="cache" value="true" />
    <property name="prefix" value="" />
    <property name="suffix" value=".vm" />
    </bean>
发布了15 篇原创文章 · 获赞 3 · 访问量 109