Spring mvc作为一个web mvc框架的后起之秀,其易用性,拓展性则实让人在使用之余,赞叹不已。本文从Spring mvc的Controller的执行过程中,找出一些开发者用到的几个拓展点。
首先,按先后顺序列一下Spring mvc中controller的执行过程:
1. 执行所有的拦截器(实现HandlerInterceptor接口的类);
2. 调用controller方法之前,对方法参数进行解释绑定(实现WebArgumentResolver接口,spring3.1以后推荐使用HandlerMethodArgumentResolver);
3. 调用controller方法,返回逻辑视图(通常是一个视图的名称);
4. 将逻辑视图映射到物理视图(使用实现ViewResolver接口的类处理);
5. 物理视图渲染输出到客户端(实现View接口);
6. 若在以上执行过程中抛出了异常,可以自定义异常处理器对不同的异常进行处理(实现HandlerExceptionResolver接口)。
开发者通过实现以上的接口(一般情况下不需要从接口实现,通过继承Spring mvc实现的一些抽象类更简单),就可以自定义controller执行过程中的一些行为,从而让程序更富灵活性。
以下是几个常用的例子:
1. 自定义拦截器,拦截未登录用户的请求。
AuthorityInterceptor.java
@Component public class AuthorityInterceptor extends HandlerInterceptorAdapter{ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Integer userId = (Integer)request.getSession().getAttribute("userId"); //登录才可以继续访问 if (userId != null) { return true; } String contextPath = request.getContextPath(); response.sendRedirect(contextPath + "/index.html"); return false; } }
applicationContext-mvc.xml添加配置:
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/user/**"/> <ref bean="authorityInterceptor"/> </mvc:interceptor> </mvc:interceptors>
值得一提的是:HandlerInterceptor接口有三个方法,preHandle在controller方法调用前拦截,postHandle在controller方法调用后拦截,afterCompletion在客户端请求处理结束后(controller方法调用后,返回视图,并且视图已经渲染输出后)拦截。
2. 自定义controller方法参数的解释器,从session从取值绑定到方法参数。
SessionValue.java
@Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface SessionValue { String value() default ""; }
SessionValueResolver.java
@Component public class SessionValueResolver implements WebArgumentResolver { @Override public Object resolveArgument(MethodParameter parameter, NativeWebRequest webRequest) throws Exception { SessionValue svAnnotation = parameter.getParameterAnnotation(SessionValue.class); if(svAnnotation == null) { return WebArgumentResolver.UNRESOLVED; } return _resolveArgument(parameter, webRequest); } private Object _resolveArgument(MethodParameter parameter, NativeWebRequest webRequest) throws Exception { SessionValue sessionValueAnnot = parameter.getParameterAnnotation(SessionValue.class); String attrName = sessionValueAnnot.value(); if(attrName == null || attrName.equals("")) { attrName = parameter.getParameterName(); } HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); Object value = request.getSession().getAttribute(attrName); if(value == null) { throw new Exception("SessionValueResolver: session 内没有该属性:" + attrName); } return value; } }
applicationContext-mvc.xml添加配置:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="customArgumentResolvers"> <list> <ref bean="sessionValueResolver"/> </list> </property> <property name="order" value="1"></property> </bean>
在controller中使用:
@RequestMapping("/get") public String getUser(@SessionValue("userId")Integer userId) { //do something return "user"; }
3. 自定义文件视图解释器,在controller方法里返回文件路径,实现文件下载
FileViewResolver.java
public class FileViewResolver extends AbstractCachingViewResolver implements Ordered{ private int order = Integer.MAX_VALUE; @Override protected View loadView(String viewName, Locale locale) throws Exception { if(viewName.startsWith(FileView.FILE_VIEW_PREFIX)) { return new FileView(viewName); } return null; } @Override public int getOrder() { return this.order; } public void setOrder(int order) { this.order = order; } }
FileView.java
public class FileView extends AbstractView { public static final String FILE_VIEW_PREFIX = "file:"; private String viewName; public FileView(String viewName) { this.viewName = viewName; } @Override protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { File file = getOutputFile(); downloadFile(request, response, file); } private File getOutputFile() throws Exception { Integer beginIndex = viewName.indexOf(FILE_VIEW_PREFIX) + FILE_VIEW_PREFIX.length(); String filePath = viewName.substring(beginIndex).trim(); File file = new File(filePath); if(file.exists()) { return file; } throw new Exception("下载的文件不存在: " + filePath); } private void downloadFile(HttpServletRequest request, HttpServletResponse response, File file) { //将文件写入输出流 } }
applicationContext-mvc.xml添加配置:
<bean class="com.plugin.FileViewResolver"> <property name="order" value="1" /> </bean>
在controller中使用:
@RequestMapping("/download") public String download() { String filePath = "f://download/text.txt"; return "file:" + filePath; }
4. 自定义异常处理,抛出以下异常时,返回错误页面
CustomExceptionResolver.java
@Component public class CustomExceptionResolver implements HandlerExceptionResolver { private static List<String> exceptionList; static{ exceptionList = new ArrayList<String>(); exceptionList.add("InvalidExcelException"); exceptionList.add("NoSuchCourseExcption"); exceptionList.add("NoTraineeExcption"); exceptionList.add("NoQuestionExcption"); } @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { if(exceptionList.contains(ex.getClass().getSimpleName())) { return new ModelAndView("error"); } return null; } }
自定义异常处理器只需要成为spring容器的bean,无需其它配置。
Spring mvc使用拓展点介绍结束。
各位Oscer,若有关于Spring mvc使用的优秀实践,请不吝分享,分享快乐。