[ SSH框架 ] Struts2框架学习之四(自定义拦截器)

2018-05-22 17:06:59 浏览数 (1)

一、Struts2的拦截器

1.1 拦截器概述

   拦截器,在AOP( Aspect-Oriented Programming)中用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作。拦截是AOP的一种实现策略。

  在 Webwork的中文文档的解释为—拦截器是动态拦截 Action调用的对象。它提供了一种机制可以使开发者可以定义在一个 action执行的前后执行的代码,也可以在一个 action执行前阻止其执行。同时也是提供了一种可以提取 action中可重用的部分的方式。

  谈到拦截器,还有一个词大家应该知道—拦截器链( Interceptor Chain,在 Struts2中称为拦截器栈 Interceptor Stack)。拦截器链就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。

1.2 拦截器实现原理

  大部分时候,拦截器方法都是通过代理的方式来调用的。Struts2的拦截器实现相对简单。当请求到达 Struts2的ServletDispatcher时, Struts2会查找配置文件,并根据其配置实例化相对的拦截器对象,然后串成一个列表,最后一个一个地调用列表中的拦截器。

  Struts2拦截器是可插拔的,拦截器是AOP的一种实现。Struts2拦截器栈就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时, Struts2拦截器链中的拦截器就会按其之前定义的顺序被调用。

1.3 Struts2的执行流程

1.4 自定义拦截器

在程序开发中,如果需要开发自己的拦截器类,就需要直接或间接的实现 com.opensymphony.xwork2.interceptor.MethodFilterInterceptor接口,自定义代码如下:

代码语言:javascript复制
public interface Interceptor extends Serializable{

        void init();
    void destory();
    String intercept(ActionInvocation invocation) throws Exception;

}    

该接口提供了三个方法,其具体介绍如下。

●  void init( ):该方法在拦截器被创建后会立即被调用,它在拦截器的生命周期内只被调用次.可以在该方法中对相关资源进行必要的初始化。

●  void destroy( ):该方法与init方法相对应,在拦截器实例被销毁之前,将调用该方法来释放和拦截器相关的资源。它在拦截器的生命周期内,也只被调用一次。

●  String intercept( ActionInvocation invocation) throws Exception;该方法是拦截器的核心方法,用来添加真正执行拦截工作的代码,实现具体的拦截操作。它返回一个字符串作为逻辑视图,系统根据返回的字符串跳转到对应的视图资源。每拦截一个动作请求,该方法就会被调用一次。该方法的 ActionInvocation参数包含了被拦截的 Action的引用,可以通过该参数的 invoke方法,将控制权转给下一个拦截器或者转给 Action的 execute( )方法。

  如果需要自定义拦截器,只需要实现 interceptor接口的三个方法即可。然而在实际开发过程中除了实现 Interceptor接口可以自定义拦截器外,更常用的一种方式是继承抽象拦截器类AbstractIntercepter该类实现了 Interceptor接口,并且提供了ini( )方法和 destroy( )方法的空实现。使用时,可以直接继承该抽象类,而不用实现那些不必要的方法。拦截器类 AbstractInterceptor中定义的方法如下所示:

代码语言:javascript复制
public abstract class Abstractinterceptor implements Interceptor{

    public void init();

    public void destroy();

    public abstract String intercept(ActionInvocation invocation);

    throws Exception;
}

  从上述代码中可以看出, AbstractInterceptor类已经实现了 Interceptor接口的所有方法,一般情况下,只需继承 AbstractInterceptor类,实现 interceptor方法就可以创建自定义拦截器。

  只有当自定义的拦截器需要打开系统资源时,才需要覆盖 AbstractInterceptor类的 init( )方法和destroy( )方法。与实现 Interceptor接口相比,继承 AbstractInterceptor类的方法更为简单。

1.5 拦截器的配置

●  拦截器

要想让拦截器起作用,首先要对它进行配置。拦截器的配置是在 struts. xml文件中完成的,它通常以< Interceptor>标签开头,以</ interceptor>标签结束。定义拦截器的语法格式如下:

代码语言:javascript复制
<interceptor name=interceptorName" class="interceptorclass"">

    <param name="paramname">paramvalue</param>

</interceptor>

上述语法格式中,name属性用来指定拦截器的名称, class属性用于指定拦截器的实现类。有时在定义拦截器时需要传入参数,这时需要使用< param>标签,其中name属性用来指定参数的名称,paramvalue表示参数的值。

●  拦截器栈

在实际开发中,经常需要在 Action执行前同时执行多个拦截动作,如:用户登录检查、登录日志记录以及权限检查等,这时,可以把多个拦截器组成一个拦截器栈。在使用时,可以将栈内的多个拦截器当成一个整体来引用。当拦截器栈被附加到一个 Action上时,在执行 Action之前必须先执行拦截器栈中的每一个拦截器定义拦截器栈使用< . interceptors>元素和< Interceptor- stack>子元素,当配置多个拦截器时,需要使用< kinterceptor-ref>元素来指定多个拦截器,配置语法如下:

代码语言:javascript复制
<interceptors>

    <interceptor-stack name="interceptorstackname">

        <interceptor-ref name="interceptorname"/>

    </interceptor-stack>

</interceptors>

在上述语法中, interceptorstackname值表示配置的拦截器栈的名称;interceptorName值表示拦截器的名称。除此之外,在一个拦截器栈中还可以包含另一个拦截器栈。

1.6 编写自定义拦截器

下面用一个添加登录拦截器功能的实例展示自定义拦截器的使用。

●  第一步:创建类,继承MethodFilterInterceptor类并重写MethodFilterInterceptor类里的方法写拦截器逻辑

代码语言:javascript复制
package com.Kevin.interceptor;
/**
 * 给登录判断写一个自定义拦截器
 */

import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;

public class LoginInterceptor extends MethodFilterInterceptor{

    //编写拦截器逻辑
    protected String doIntercept(ActionInvocation invocation) throws Exception {                
        //判断session里是否有username的值
        //得到session
        HttpServletRequest request=ServletActionContext.getRequest();
        Object obj=request.getSession().getAttribute("username");
        //判断
        //登录状态
        //做放行操作,执行action方法
        if(obj!=null)            
            return invocation.invoke();    
        //不是登录状态
        //不到登录,不执行action方法,返回登录页面
        //到result标签里找到login的值,到配置路径里
        else            
            return "login";    
    }

}

●  第二步:配置action和拦截器关系(注册拦截器)

代码语言:javascript复制
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    "http://struts.apache.org/dtds/struts-2.3.dtd">
    
<struts>
    <constant name="struts.i18n.encoding" value="UTF-8"></constant>
    
    <package name="strutsdemo" extends="struts-default" namespace="/">
    
        <!-- 1.声明拦截器 -->
        <interceptors>
            <interceptor name="loginintercept" class="com.Kevin.interceptor.LoginInterceptor"></interceptor>
        </interceptors>
    
        <action name="customer_*" class="com.Kevin.action.LoginAction" method="{1}">
            <!-- 2.使用自定义拦截器 -->
            <interceptor-ref name="loginintercept">
                <!-- 配置action中某些方法不进行拦截
                    name属性值:excludeMethods
                    值:action不拦截的方法名称
                 -->
                 <param name="excludeMethods">login</param>
            </interceptor-ref>
            
            <interceptor-ref name="defaultStack"></interceptor-ref>
            
            <!-- 登录失败 -->
            <result name="login">/login.jsp</result>
            <!-- 登录成功 -->
            <result name="loginsuccess">/welcome.jsp</result>
            <result name="menu">/menu.jsp</result>
            
        </action>
        
        <action name="welcome" class="com.Kevin.action.TestAction">
            <result name="success">/welcome.jsp</result>
        </action>    
        
    </package>
    
</struts>

Tips:struts2里有很多默认的拦截器,但是如果在action里配置了自定义拦截器,默认拦截器就不会在执行。       解决办法:将默认拦截器手动使用,如代码<interceptor-ref name="defaultStack"></interceptor-ref>

判断登录action编写:

代码语言:javascript复制
package com.Kevin.action;
/**
 * 登录判断Action
 */
import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class LoginAction extends ActionSupport{
    
    //登录的方法
    public String login(){
        //1.得到request对象
        HttpServletRequest request=ServletActionContext.getRequest();
        String username=request.getParameter("username");
        String password=request.getParameter("password");
        //2.判断用户名和密码
        if("admin".equals(username) && "admin".equals(password)){
            //登陆成功
            request.getSession().setAttribute("username", username);
            return "loginsuccess";
        }
        
        else
            //登录失败
            return "login";
    }
    
    //测试拦截器进入menu页面时,是否进行登录拦截
    public String menu(){
        return "menu";
    }  
    @Override
    public String execute() throws Exception {
        
        return null;
    }

}

Tips:拦截器和过滤器的区别:(重要概念)     1.过滤器:理论上可以过滤任意内容,比如html、jsp、servlet、图片路径;     2.拦截器:拦截器可以拦截action。

Tips:Servlet和Action的区别:     1.servlet默认第一次访问时候创建,创建一次,单实例对象;     2.action每次访问时侯创建,创建多次,多实例对象。

二、Struts2的标签库

2.1 Struts2标签库概述

 对于一个MVC框架而言,重点是实现两部分:业务逻辑控制器部分和视图页面部分。 Struts2作为一个优秀的MVC框架,也把重点放在了这两部分上。控制器主要由 Action来提供支持,而视图则是由大量的标签来提供支持。接下来将针对 Struts2标签库的构成和常用标签的使用进行详细的讲解。

2.2 Struts2标签库分类

  早期的JSP页面需要嵌入大量的Java脚本来进行输出,这样使得一个简单的JSP页面加入了大量的代码,不利于代码的可维护性和可读性。随着技术的发展,逐渐的采用标签库来进行JSP页面的开发,这使得JSP页面能够在很短的时间内开发完成,而且代码通俗易懂,大大的方便了开发者Struts2的标签库就是这样发展起来的。

  Struts2框架对整个标签库进行了分类,按其功能大致可分为两类,如图所示。

由图中可以看出, Struts2标签库主要分为两类:普通标签UI标签。普通标签主要是在页面生成时,控制执行的流程。UI标签则是以丰富而可复用的HTML文件来显示数据。

  普通标签又分为控制标签( Control Tags)和数据标签( Data Tags)。控制标签用来完成条件逻辑、循环逻辑的控制,也可用来做集合的操作。数据标签用来输出后台的数据和完成其他数据访问功能。

  UI标签又分为表单标签( Form Tags)、非表单标签( non-form Tags)和Ajax标签。表单标签主要用来生成HTML页面中的表单元素,非表单标签主要用来生成HTML的<div>标签及输出 Action中封装的信息等。Ajax标签主要用来提供Ajax技术支持。

2.3 Struts2标签的使用

  Struts2标签库被定义在 struts-tags tld文件中,我们可以在 struts-core-2.3.24jar中的META-INF目录下找到它。要使用 struts2的标签库,一般只需在JsP文件使用 taglib指令导入 Struts2标签库,具体代码如下:

代码语言:javascript复制
<%@taglib prefix="s" uri="/struts-tags" %>

在上述代码中, taglib指令的uri属性用于指定引入标签库描述符文件的URI, prefix属性用于指定引入标签库描述符文件的前缀。需要注意的是,在JSP文件中,所有的 Struts2标签库的使用“s”前缀。

2.4 Struts2的控制标签

●  <s:if>、<s: elseif>、<s:else>标签

与多数编程语言中的if、 elseif和else语句的功能类似,<s:if>、<s: elseif>、<s:else>这三个标签用于程序的分支逻辑控制。其中,只有<s:if>标签可以单独使用,而< s elseif>、<s:else>都必须与<sf>标签结合使用,其语法格式如下所示:

代码语言:javascript复制
<s: if test="表达式1">

    标签体

</s: if>

<s:elseif test="表达式2">

    标签体

</s: elseif>

<s:e1se>

    标签体

</s :else>

上述语法格式中,<s:if>、和<s: elseif>标签必须指定test属性,该属性用于设置标签的判断条件,其值为 boolean型的条件表达式。

●  < s:iterator>标签

< s:iterator>标签主要用于对集合中的数据进行迭代,它可以根据条件遍历集合中的数据。<s:iterator>标签的属性及相关说明如表所示:

在表中,如果在< s:Iterator>标签中指定 status属性,那么通过该属性可以获取迭代过程中的状态信息,如:元素数、当前索引值等。通过 status属性获取信息的方法如表所示(假设其属性值为st):

2.5 Struts2的数据标签

●  < s:property>标签

<s:property>标签用于输出指定的值,通常输出的是 value属性指定的值,< s:property>标签的属性及属性说明如下所示:

●  id:可选属性,指定该元素的标识。

●  default:可选属性,如果要输出的属性值为nul,则显示 default属性的指定值

●  escape:可选属性,指定是否忽略HTM代码。

●  value:可选属性,指定需要输出的属性值,如果没有指定该属性,则默认输出 Valuestack栈顶的值。

●  <s:a>标签

<s:a>标签用于构造HTML页面中的超链接,其使用方式与 HTML中的<a>标签类似。<s:a>标签的属性及相关说明如表所示:

●  <s:debug>标签

<s:debug>标签用于在调试程序时输出更多的调试信息,主要输出 ValueStack和 StackContext中的信息,该标签只有一个id属性,且一般不使用在使用 debug标签后,网页中会生成一个[ Debug]的链接,单击该链接,网页中将输出各种服务器对象的信息,如图所示:

2.6 Struts2的表单标签

  Struts2的表单标签用来向服务器提交用户输入的信息,绝大多数的表单标签都有其对应的HTML标签,通过表单标签可以简化表单开发,还可以实现HTM江中难以实现的功能。大家可以结合HTML的标签对比学习 Struts2的表单标签。

● 表单标签的公共属性

Struts2的表单标签用来向服务器提交用户输入信息,在 org. apache struts2 components包中都有个对应的类,所有表单标签对应的类都继承自 Uibean类。 Uibean类提供了一组公共属性,这些属性是完全通用的。如表所示:

  除了这些常用的通用属性外,还有很多其它属性。由于篇幅有限,这里就不一一列举。需要注意的是,表单标签的name和 value属性基本等同于HTML组件的name和 value,但是也有些不同的地方:表单标签在生成HTML的时候,如果标签没有设置 value属性的话,就会从值栈中按照name获取相应的值,把这个值设置成的HTML组件的 value。简单的说,就是表单标签的 value在生成HTML的时候会自动设置值,其值从值栈中获取。

●  <s:form>标签

<s:form>标签用来呈现HTML语言中的表单元素,其常用属性如表所示:

在使用< s: form>标签时,一般会包含其它的表单元素,如 textfield, radio等标签,通过这些表单元素对应的name属性,在提交表单时,将其作为参数传入 Struts2框架进行处理。

●  <s:submit>标签

  < s: submit>标签主要用于产生HTML中的提交按钮,该表单元素中,可以指定提交时的 Action对应的方法。通常与< s: forn>标签一起使用,该标签的常用属性如表所示:

●  <s:textfield>和<s:textarea>标签

<s: textfield>和<s: textarea>标签的作用比较相似,都用于创建文本框,区别在于<s:textfield>创建的是单行文本框,而< s:textarea>创建的是多行文本框。二者使用也比较简单,一般指定其 label和name属性即可。两个标签的用法如下所示

<s:textfield>标签的用法:

代码语言:javascript复制
<s:textfield label="用户名" name="username"/>

< s:textarea>标签的用法:

代码语言:javascript复制
<s:textarea label="Message"  name="message"/>

  name属性用来指定单行/多行文本框的名称,在 Action中,通过该属性获取单行/多行文本框的值。其 value属性用来指定单行/多行文本框的当前值。此外,< textarea>标签可以通过使用cols和rows属性分别指定多行文本框的列数和行数。

●  <s:password>标签

<s:password>标签用于创建一个密码输入框,它可以生成HTML中的<input type="password"/>标签,常用于在登录表单中输入用户的登录密码。<s:password>标签的常用属性说明如表所示:

  需要注意的是 Struts2的 password标签与HTML的< input type="password"/>标签有小小的不同,< input type= "password">标签只要设置 value属性就可以将 value属性的值作为默认显示值;而 Struts2的< s:password>标签除了要设置 value属性,还要设置 showPassword属性为true。

●  <s:radio>标签

  < s:radio>标签用于创建单选按钮,生成HTML中的< cinput type= radio”个>标签。< s:radio>标签的常用属性说明如表所示:

●  <s:checkboxlist>标签

   <s:checkboxlist>标签用于一次性创建多个复选框,用户可以选创建零到多个,它用来产生一组<input type="checkbox"/>标签,常用属性说明如表示:

●  <s:select>标签

   <s:select>标签用于创建一个下拉列表,生成HTML中的<select>标签,常用属性说明如表所示:

  在表中, headerkey和header value属性需要同时使用,可以在所有的真实选项之前加一项作为标题项。比如选择省份的时候,可以在所有的具体省份之前加一项“请选择”,这个项不作为备选的值。

  multiple属性和size属性类似于HTML的< select>标签,size属性可以让下拉框同时显示多个值multiple属性让用户同时选择多个值,只是在后台的 Action接收下拉框值的时候,不能使用 String类型,而应该使用 String[ ]或者List<String>。

●  <s:hidden>标签

  < s:hidden>标签用于创建隐藏表单元素,生成HTML中的隐藏域标签< input type=" hidden”>。该标签在页面上没有任何显示,可以保存或交换数据。其使用也比较简单,通常只设置其name和 value属性即可。其一般用法如下所示:

代码语言:javascript复制
<s: hidden name="id" value="%{id}"/>

该标签主要用来需要提交的表单传值时使用,比如需要提交表单时,要传一个值到请求参数中去,就可以使用该标签。

●  <s:reset>标签

  < s:reset>标签用来创建一个重置按钮,会生成HTML中的< input type= "reset"}>标签,该标签的使用比较简单,其常用属性为name和 value。其中name属性用于指定重置按钮的名称,在 Action中,可以通过name属性来获取重置按钮的值,value属性用于显示按钮的值。该标签的用法如下所示:

代码语言:javascript复制
<s:reset value="Reset"/>

 下面通过一个综合JSP页面示例来展示form表单标签的用法:

代码语言:javascript复制
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<html>
  <head>
    <title>My JSP 'form.jsp' starting page</title>
  </head>
  <body>
    <!-- form标签 -->
    <s:form>
        <!-- 1.普通输入项 -->
        <s:textfield name="username" label="Username"></s:textfield>
        
           <!-- 2.密码输入项 -->
        <s:password name="password" label="Password"></s:password>
        
           <!-- 3.单选输入项 -->    
        <!-- value属性值与显示值一样 -->
        <s:radio list="{'Male','Female'}" name="gender" label="Gender"></s:radio>
        <!-- value属性值与显示值不一样 -->
        <s:radio list="#{'M':'男','F':'女'}" name="gender2" label="Gender"></s:radio>
        
        <!-- 4.复选输入项 -->
        <!-- value属性值与显示值一样 -->
        <s:checkboxlist list="{'Basketball','Football','Baseball','PingPang'}" name="hobby" label="Hobby"></s:checkboxlist>
        <!-- value属性值与显示值不一样 -->
        <s:checkboxlist list="#{'Basketball':'BSKTball','Football':'FTball','Baseball':'BSball','PingPang':'PP' }" name="hobby2" label="Hobby"></s:checkboxlist>
        
        <!-- 5.下拉输入框 -->
        <s:select list="{'C','C  ','Java','Python'}" name="language" label="Programming"></s:select>
        
        <!-- 6.隐藏项 -->
        <s:hidden name="hid" label="hidden"></s:hidden>
        
        <!-- 文件上传 -->
        <s:file name="file" value="Upload"></s:file>
        
        <!-- 7.文本域 -->
        <s:textarea rows="30" cols="60" name="message" label="Message"></s:textarea>
        
        <!-- 8.提交按钮-->
        <s:submit value="Submit"></s:submit>
        
        <!-- 9.重置按钮 -->
        <s:reset value="Reset"></s:reset>
    </s:form>

  </body>
</html>

显示如下:

0 人点赞