一、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>
显示如下: