首页 > 代码库 > Struts2.x教程(三) Struts2拦截器

Struts2.x教程(三) Struts2拦截器

一、Struts2拦截器介绍
    Struts2拦截器是使用AOP实现的,主要是针对action对象进行拦截,可以在访问action的某个方法、字段之前或之后实施拦截。
    可以为action配置多个拦截器,Struts2会将这一组拦截器按照一定顺序组织成一个拦截器栈。action可以直接引用某个拦截器栈来实现配置多个拦截器的目的。
 
    对于继承struts_default的package中的action,都会默认引用name=defaultStack的拦截器栈(在struts_default中定义了Struts2提供的各种拦截器),action对于拦截器的调用顺序参考下图:
 
 
二、拦截器实现
    Struts2的拦截器都实现了com.opensymphony.xwork2.interceptor.Interceptor接口:
public interface Interceptor extends Serializable {
    //销毁方法
    void destroy();
    //初始化方法
    void init();
    //拦截方法
    String intercept(ActionInvocation invocation) throws Exception;
}
我们要实现一个自定义的拦截器,就要实现Interceptor接口,并主要实现intercept()方法。例如下面,我们实现一个简单的认证拦截器:
public class AuthorityInterceptor implements Interceptor {
    
    public void init() {
    }
    
    public void destroy() {
    }
    
    @Override
    public String intercept(ActionInvocation invocation) throws Exception {
        Object user = ActionContext.getContext().getSession().get("user");
        if (user == null) {
            System.out.println("未登录,请先登录!");
            return "login";
        }
        return invocation.invoke();
    }
}
 
在自定义的拦截器中的intercept()中,我们可以返回一个字符串Result,此时请求会终止并跳转到对应的result。
invocation是一个ActionInvocation的引用,ActionInvocation负责调用该请求的action及其一系列拦截器,当我们调用invocation.invoke()时,它会判断此时拦截器栈是否还有拦截器,是的话会调用下一个拦截器的拦截方法,否则会调用action对应的请求方法。
 
以上,我们就实现了一个自定义的拦截器。
 
要让这个自定义的拦截器工作,就需要在struts.xml配置文件中进行配置:
首先,要在package元素下,对拦截器进行声明:
        <interceptors>
            <interceptor name="authorityInterceptor" class="com.boya.struts2.interceptor.AuthorityInterceptor"></interceptor>
        </interceptors>
然后,在action元素中引用这个拦截器就可以了,如:
         <action name="user" class="com.boya.struts2.web.UserAction">
             <interceptor-ref name="authorityInterceptor" />
             <result>/success.jsp</result>
         </action>
 
但是,需要注意的是:
当在action元素中设置了<interceptor-ref name="authorityInterceptor" />,它会覆盖默认的defaultStack拦截器栈的引用。假设自定义的拦截器仅仅是一个日志拦截,那么这样配置,defaultStack中的拦截器都不会调用,比如最重要的参数拦截器ParametersInterceptor,调用action时就会出现问题。因此在对action添加拦截器引用时,通常这样配置:
         <action name="user" class="com.boya.struts2.web.UserAction">
              <interceptor-ref name="defaultStack" />
              <interceptor-ref name="authorityInterceptor" />
             <result>/success.jsp</result>
         </action>
 
三、使用拦截器实现文件上传
Struts 2是使用commons-fileupload进行文件上传的,首先使用commons-fileupload将文件上传到临时目录中,然后Struts2拦截器将文件信息封装入action对象,然后以操作本地文件的方式完成指定目录的上传。
文件上传实现步骤:
1、页面设置文件上传表单
<s:form action="doUpload" method="POST" enctype="multipart/form-data">
    <s:file name="upload" label="File"/>
    <s:submit/>
</s:form>
2、编写文件上传Action
public class FileUploadAction extends ActionSupport {
    private static final long serialVersionUID = 5156288255337069381L;
    private String contentType;    //上传文件的类型
    private File upload;    //上传文件
    private String fileName;    //上传文件的名称
    private String caption;    //提交的相关信息
    public String input() throws Exception {
        return SUCCESS;
    }
    public String upload() throws Exception {
        String path = "upload\\";
        InputStream in = new FileInputStream(upload);
        String contextPath = ServletActionContext.getServletContext().getRealPath("/");
        File dir = new File(contextPath);
        if (!dir.exists()) {
            dir.mkdir();
        }
        String filePath = contextPath + path + fileName;
        OutputStream out = new FileOutputStream(filePath);
        int bytesRead;
        byte[] buffer = new byte[8192];
        while ((bytesRead = in.read(buffer, 08192)) != -1) {
            out.write(buffer, 0, bytesRead);
        }
        out.close();
        in.close();
        
        return SUCCESS;        
    }
    public String getUploadFileName() {
        return fileName;
    }
    public void setUploadFileName(String fileName) {
        this.fileName = fileName;
    }
    public String getUploadContentType() {
        return contentType;
    }
    public void setUploadContentType(String contentType) {
        this.contentType = contentType;
    }
    public File getUpload() {
        return upload;
    }
    public void setUpload(File upload) {
        this.upload = upload;
    }
    public String getCaption() {
        return caption;
    }
    public void setCaption(String caption) {
        this.caption = caption;
    }
}
注意:拦截器会将文件名称及文件的编码类型封装入uploadFileName和uploadContentType属性中,其中upload是对应页面中file标签的name名称,FileName和ContentType是固定后缀,因此getter和setter方法要对应这两个名称,否则无法对文件信息进行封装。
 
3、在struts.xml配置文件中,配置action请求即可
        <action name="upload" class="com.boya.ssh.web.FileUploadAction" method="input">
            <result>/upload.jsp</result>
        </action>
        <action name="doUpload" class="com.boya.ssh.web.FileUploadAction" method="upload">
            <result name="input">/upload.jsp</result>
            <result>/upload-success.jsp</result>
        </action>
 
注意:
    a、Struts2是使用拦截器来完成文件上传的,而因为action默认引用的defaultStack拦截器栈中已经包含了fileUpload拦截器,因此无需再添加文件上传拦截器的引用。
    b、Struts2默认上传文件最大为2M,可以通过<constant name="struts.multipart.maxSize" value="20971520/> 来修改最大上传文件的限制(修改为20M,单位为字节)
 
四、重复提交拦截器
在Struts2中防止重复提交机制也是通过拦截器实现的,它的原理是:
1、在页面中添加<s:token />标签:
    它会在session中生成一个key为struts.token的随机字符串,
    同时在页面添加对应的隐藏于标签,如:
        <input type="hidden" name="struts.token.name" value=http://www.mamicode.com/"token" />
        <input type="hidden" name="token" value=http://www.mamicode.com/"448CL36HMFSEORYB9X12ZHU8B10W87U9" />
2、提交表单时,同时将隐藏域进行提交,在token拦截器中对提交的token值和session中保存的token值进行比较:
    如果相等,执行后续请求处理,同时将session中的token清空。
    如果不等,则拦截请求。
它正是通过这种方式对重复的请求进行拦截,以防止重复提交。
 
Struts2提供了两种重复提交拦截器实现;
    token:出现重复提交后跳转到指定的页面,需要配置<result name="invalid.token">/input</result>
    tokenSession:重复提交时按钮无效,页面不会调整,第一次的请求完成后跳转到成功页面
对比这两种拦截机制,tokenSession适用性更强些。
 
实现步骤:
1、页面添加<s:token />,放在form表单内即可。
    Struts1的token标签必须使用<html:form />,而Struts2已经没有这个限制了,普通的html标签仍然可以使用<s:token />来实现防止重复提交。
2、在struts.xml中添加拦截器引用,defaultStack拦截器栈中没有token拦截器,因此需要添加引用
        <action name="saveUser" class="com.boya.ssh.web.UserAction" method="save">
            <interceptor-ref name="defaultStack" />
            <interceptor-ref name="token" />
            <result name="invalid.token">/user-input</result>
            <result>/user.jsp</result>
        </action>
使用tokenSession拦截器则无需配置invalid.token:
        <action name="saveUser" class="com.boya.ssh.web.UserAction" method="save">
            <interceptor-ref name="defaultStack" />
            <interceptor-ref name="tokenSession" />
            <result>/user.jsp</result>
        </action>
 
以上就是使用拦截器实现的防止重复提交问题。