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;
}
转载于:https://blog.51cto.com/yoyanda/1715096