struct2源码解读之拦截器
上篇博文介绍了拦截器的工作原理:通过invocation.invoke()方法迭代拦截器链。那么拦截器链里面的拦截器有什作用呢?下面为大家一一探讨。我们先来看下默认拦截器链有哪些拦截器。
我们找到struct-default.xml文件
<interceptor-stack name="defaultStack"> <interceptor-ref name="exception"/> <interceptor-ref name="alias"/> <interceptor-ref name="servletConfig"/> <interceptor-ref name="i18n"/> <interceptor-ref name="prepare"/> <interceptor-ref name="chain"/> <interceptor-ref name="debugging"/> <interceptor-ref name="scopedModelDriven"/> <interceptor-ref name="modelDriven"/> <interceptor-ref name="fileUpload"/> <interceptor-ref name="checkbox"/> <interceptor-ref name="multiselect"/> <interceptor-ref name="staticParams"/> <interceptor-ref name="actionMappingParams"/> <interceptor-ref name="params"> <param name="excludeParams">dojo\..*,^struts\..*</param> </interceptor-ref> <interceptor-ref name="conversionError"/> <interceptor-ref name="validation"> <param name="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> <interceptor-ref name="workflow"> <param name="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> </interceptor-stack>
一、exception-声明式异常处理
1.1.应用
第一个拦截器"exception"是用来声明式异常处理的。
如:我们希望在执行action方法时,如果发生异常,则跳到指定的页面,则我们可以在action中配置
<action name="user_*" class="userAction" method="{1}"> <result name="toLoginUI">/WEB-INF/jsp/forward/index/Login.jsp</result> <exception-mapping result="error" exception=""></exception-mapping> <result name="error">/WEB-INF/jsp/forward/index/error.jsp</result> </action>
当执行userAction类并发生异常时(我们可以在exception属性设置是什么样的异常),就会调到result(result属性)指定的result(result标签)。如果全部action的同类异常都想跳转到同一个页面,则可设置全局异常处理
<global-exception-mappings> <exception-mapping result="" exception=""></exception-mapping> </global-exception-mappings> <global-results> <result name="error">/WEB-INF/jsp/forward/index/error.jsp</result> </global-results>
1.2.实现原理:
我们来看看这个exception的拦截器
<interceptor name="exception" class="com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor"/>
我们找到这个类的intercept方法,因为action请求是在invocation.invoke()方法迭代执行的,这里用到了一个try-catch,当try里面的代码执行发生异常,则找到exceptionMapping,然后找到exceptionMapping里面的result,如果这个result存在,就返回这个result值,并把exceptionHolder这个对象压入值栈,后面的 的就是执行result了。
public String intercept(ActionInvocation invocation) throws Exception { String result; try { //执行action请求 result = invocation.invoke(); } catch (Exception e) { if (isLogEnabled()) { handleLogging(e); } //找到actionConfig里面的exceptionMapping List<ExceptionMappingConfig> exceptionMappings = invocation.getProxy().getConfig().getExceptionMappings(); //找到exceptionMapping里面的result String mappedResult = this.findResultFromExceptions(exceptionMappings, e); if (mappedResult != null) { result = mappedResult; //如果找到了,就把exceptionHolder对象压入值栈 publishException(invocation, new ExceptionHolder(e)); } else { throw e; } } return result; }
所以在xml中配置exceptionMapping就能达到声明式异常管理。
二、servletConfig-完成request注入
2.1.应用
servletConfig拦截器是用来获取request\session\application等对象的。在struct2中获取request、session、application对象,只需继承RequestAware,SessionAware接口即可
public abstract class BaseAction<T> extends ActionSupport implements ModelDriven<T>,RequestAware,SessionAware{ protected Map<String, Object> request; protected Map<String, Object> session; //get()和set()方法略 }
这样就能直接在子类中用Map<string,object> request去操作request等对象了
public void exit() throws Exception { //把user从session中移除 session.remove("user"); }
2.2.实现原理
为什么实现了RequestAware,SessionAware等接口就能获取相应的对象了呢?我们也来看看这个拦截器的intercept()方法
<interceptor name="servletConfig" class="org.apache.struts2.interceptor.ServletConfigInterceptor"/>
从这个拦截器中可以看出,struct2会先对这些action判断是否是这些接口的实例,如果是话就用set()方法,把context里面的request注入到map requset变量中。所以通过实现这些接口,就可以获得相应的对象。
public String intercept(ActionInvocation invocation) throws Exception { final Object action = invocation.getAction(); final ActionContext context = invocation.getInvocationContext(); //ServletRequest if (action instanceof ServletRequestAware) { HttpServletRequest request = (HttpServletRequest) context.get(HTTP_REQUEST); ((ServletRequestAware) action).setServletRequest(request); } //Application if (action instanceof ApplicationAware) { ((ApplicationAware) action).setApplication(context.getApplication()); } //Session if (action instanceof SessionAware) { ((SessionAware) action).setSession(context.getSession()); } //Request if (action instanceof RequestAware) { ((RequestAware) action).setRequest((Map) context.get("request")); } return invocation.invoke(); }
从这个拦截器可以看到,我会基本可以获取到所有的http对象,比如说request,session,application,servletRequest,Parameter等等。
三、fileupload-文件上传
3.1.应用
fileupload这个拦截器是用来文件上传用的。当我们需要上传文件的时候,在action定义几个变量,就能获取上传文件的全部信息。
private File upload; //上传的文件 private String uploadContentType; //上传文件类型 protected String uploadFileName;// 上传文件名 //这个uoload是<input name="upload">相一致 //get()和set()方法略。
3.2.实现原理
为什么定义了这些变量就能获取文件了呢?我们来看看这个拦截器的实现
<interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/>
在这个拦截器的intercept()方法中,这个方法很多内容,我就截取几段关键代码
public String intercept(ActionInvocation invocation) throws Exception { if (!(request instanceof MultiPartRequestWrapper)) { //如果不是文件类型,下一个拦截器 return invocation.invoke(); } //文件类型的request MultiPartRequestWrapper multiWrapper = (MultiPartRequestWrapper) request; Enumeration fileParameterNames = multiWrapper.getFileParameterNames(); while (fileParameterNames != null && fileParameterNames.hasMoreElements()) { if (isNonEmpty(contentType)) { // filename就是input的value值 String[] fileName = multiWrapper.getFileNames(inputName); if (isNonEmpty(fileName)) { // get a File object for the uploaded File File[] files = multiWrapper.getFiles(inputName); if (files != null && files.length > 0) { //这个inputName是input这个按钮里面的属性,如在<input value="upload">则fileName为uploadFileName,如果<input value="te">,则为teFileName String contentTypeName = inputName + "ContentType"; String fileNameName = inputName + "FileName"; if (!acceptedFiles.isEmpty()) { Map<String, Object> params = ac.getParameters(); //把这三个属性放到params,然后就可以根据get()取到 params.put(inputName, acceptedFiles.toArray(new File[acceptedFiles.size()])); params.put(contentTypeName, acceptedContentTypes.toArray(new String[acceptedContentTypes.size()])); params.put(fileNameName, acceptedFileNames.toArray(new String[acceptedFileNames.size()])); } } } // 下一个拦截器 String result = invocation.invoke(); fileParameterNames = multiWrapper.getFileParameterNames(); while (fileParameterNames != null && fileParameterNames.hasMoreElements()) { } return result; }