一、拦截器介绍
拦截器即interceptor
其实现类似于Servlet技术中的Filter,与AOP概念是相关的。
关于AOP可以参考:
http://www.cnblogs.com/wayfarer/articles/241024.html
http://baike.baidu.com/view/73626.htm
Servlet中的Filter技术可以参考:
http://blog.csdn.net/sun305355024sun/article/details/4815471
http://download.oracle.com/javaee/6/api/javax/servlet/Filter.html
拦截器通常封装了一些具有独立逻辑的通用功能,比如登录校验、异常捕捉、日志记录等等;利用它
可以在Action的执行前后做一些额外的工作,比如在执行前进行用户登录信息检查,若未登录跳转到登录页面,
否则执行Action。
二、拦截器的接口与实现
[java] view plaincopy
- public interface Interceptor extends Serializable{
- void destroy();
- void void init();
-
-
-
- String intercept(ActionInvocation invocation)throws Exception;
- }
AbstractInterceptor实现了该接口,并提供init和destroy方法的空实现,一般情况下实现自定义拦截器只需要
继承该类并重写intercept方法即可。
intercept方法的实现示例(登录信息检查):
[java] view plaincopy
- ActionContext context = invocation.getInvocationContext();
- Map session = context.getSession();
- String user = session.get("user");
- if(user == null){
- return Action.LOGIN;
- }else{
- return invocation.invoke();
- }
三、拦截器的配置
1 在package中定义拦截器:
[html] view plaincopy
- <interceptors>
- <interceptor name="exceptionManager" class="cn.uc.mms.action.ExceptionInterceptor"/>
- </interceptors>
2 拦截器可以组成拦截器栈,拦截器栈可以由拦截器和栈组成。
[html] view plaincopy
- <interceptor-stack name="myDefault">
- <interceptor-ref name="exceptionManager"/> //引用前面定义的拦截器
- <interceptor-ref name="defaultStack"/> //引用当前上下文的默认拦截器栈
- </interceptor-stack>
3 可以为package定义全局的拦截器引用,该定义仅支持一个引用,如果需要引用多个拦截器,可以声明为一个拦截器栈。
<default-interceptor-ref name="myDefault" />
//该声明需要在拦截器或拦截器栈定义之后,Action声明之前
4 为Action定义拦截器引用:
<interceptor-ref name="exceptionManager"></interceptor-ref>
需要注意的是Action定义了拦截器引用之后,全局的拦截器引用将失效,需要显示声明来恢复:
<interceptor-ref name="myDefault"></interceptor-ref>
5 为拦截器注入参数:
interceptor/interceptor-stack、interceptor-ref和default-interceptor-ref都可以通过param向interceptor注入参数
简单情况如:
<param name="name">拦截器A</param> //拦截器需要有name属性(定义了setName方法)即可注入。
但如果是从interceptor-stack进行参数注入,则param的name属性需要包含目标拦截器的名称前缀:
<param name="interceptorA.name">拦截器A</param>
如果同一个拦截器的param定义出现冲突,则取最后定义的值。
6 拦截器的执行顺序:
把拦截器想象成一个栈,由配置顺序构造出一个拦截器的执行列表,最底下是Action;
从第一个拦截器往下执行,执行完Action之后又从最后一个拦截器往上走。
四、实现常用的拦截功能
1 过滤方法的拦截器:
MethodFilterInterceptor,指定excludeMethods黑名单、includeMethods为白名单,多个方法以,分隔。
如果一个方法在两个名单中,那么会被拦截(被黑)
使用方式:用一个类继承该类,并进行配置即可。
2 监听Action返回的逻辑结果
实现PreResultListener的接口,并在action执行前为invocation对象添加监听;
通过拦截器实现上述动作:
[java] view plaincopy
- public class MyPreResultListener implements PreResultListener{
- public void beforeResult(ActionInvocation invocation, String resultCode){
- ...
-
- }
- }
使用拦截器在action执行前执行:
[java] view plaincopy
- invocation.addPreResultListener(new MyPreResultListener());
- <span style="color:#FF0000;">
五、最佳实践-- 实现全局异常捕获的拦截器
实现一个拦截器,捕获Action执行时发生的异常,将结果信息写入到ActionContext的值栈中,并返回ERROR视图:
拦截器实现:
[java] view plaincopy
- public class ExceptionInterceptor extends AbstractInterceptor {
- private static final Log log = LogFactory.getLog(ExceptionInterceptor.class);
-
-
- @Override
- public String intercept(ActionInvocation invocation) throws Exception {
- String actionName = invocation.getInvocationContext().getName();
- try {
-
-
-
-
- Object action = invocation.getAction();
- if (action instanceof ValidationAware) {
- ValidationAware validation = (ValidationAware) action;
- if(validation.hasFieldErrors()){
- String errorStr = JSONObject.fromObject(validation.getFieldErrors()).toString();
- throw new RuntimeException("拦截到错误:" + errorStr);
- }
- }
-
- return invocation.invoke();
- } catch (Exception ex) {
- ex.printStackTrace();
- log.error("Error by action{" + actionName + "}", ex);
- HashMap<String, String> map = new HashMap<String, String>();
- String msg = "unknown";
- if(ex.getMessage() != null){
- msg = ex.getMessage();
- }
- map.put("success", String.valueOf(false));
- map.put("error", msg);
- map.put("errorType", ex.getClass().toString());
-
- ActionContext ac = invocation.getInvocationContext();
- ValueStack stack = ac.getValueStack();
- stack.set("error", map);
- return "error";
- }
- }
- }
配置文件:
[html] view plaincopy
- <interceptors>
- <interceptor name="exceptionManager" class="action.ExceptionInterceptor"/>
- <interceptor-stack name="myDefault">
- <interceptor-ref name="exceptionManager"/>
- <interceptor-ref name="defaultStack"/>
- </interceptor-stack>
- </interceptors>
- <default-interceptor-ref name="myDefault" />
-
- <global-results>
- result name="error" type="json">
- <param name="root">error</param>
- <param name="contentType">text/html;charset=UTF-8</param>
- <param name="excludeNullProperties">true</param>
- </result>
- </global-results>
-
-
- <action name="upload" class="..." method="doUpload">
- <interceptor-ref name="fileUpload">
- <param name="allowedTypes">application/vnd.ms-excel,application/excel,application/octet-stream</param>
[html] view plaincopy
- <param name="maximumSize">8192000</param>
- </interceptor-ref>
- <interceptor-ref name="myDefault"></interceptor-ref>
- </action>
- <action name="*" class="action.{1}Action"></action>
六、struts内置拦截器
struts的拦截器是灵活的可插拔的,其通过强大的内置拦截器栈来完成大量通用的功能。
比如params拦截器将http请求参数自动注入到action的属性中;validation完成表单校验;fileUpload用于
文件上传中将文件域信息注入到action的指定属性中,同时也提供相关的条件检查(如文件大小、类型)
然而尽管内置拦截器栈是强大的,但有时也会导致一些不必要的开销,比如:
fileUpload的拦截器在默认拦截器栈中有一个,在普通用法中常常是另外声明一个fileUpload拦截器置于默认栈之前。
如此,在默认栈中的fileUpload拦截器则是多余。此时要提高性能则需自行定制。
struts内置拦截器列表及说明:
http://tsunzhang.iteye.com/blog/811565