SSH框架之旅-struts2(4)

struts.jpg

1.Struts2 拦截器


1.1 AOP 思想

AOP 是 Aspect Objected Prograing(面向切面编程)的缩写。struts2 中的拦截器就是这种编程策略的一种实现,AOP 思想是在基本功能上,不通过修改源代码就可以扩展功能,提高代码的重用性。

1.2 拦截器概述

struts2 框架的许多功能都是基于拦截器的,struts2 中有很多拦截器,默认的拦截器每次都执行。说到拦截器,还有一个种重要的概念——拦截器链(在 struts2 中称为是拦截器栈)。拦截器链就是将一堆拦截器按照一定顺序联结成一条链,在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其定义的顺序调用。

1.3 struts2 拦截器原理

struts2 的拦截是通过 xml文件的配置实现的,默认的拦截器在 struts2-core-*.jar 包中的 struts-default.xml 文件,可以查看源代码。当收到一个请求时,struts2 会先查找xml配置文件,并根据配置来实例化拦截器对象,然后串成一条链,请求要通过每一个拦截器,才能执行 Action 中的方法,最终才能得到想要的结果。这么多拦截器,使用一个代理对象对它们进行动态调用。当 action 的请求到来时,创建 Action 的代理对象,这个代理对象在 Action 方法执行之前执行默认的拦截器和其他拦截器(用户自定义的拦截器),最后才是 Action 对应方法的调用,这里面是数据结构栈的思想。代理对象调用栈的最底层才是 Action 方法的调用,然后在返回给上一个拦截器,层层退出。

struts2 拦截器结构的设计是一个典型的责任链模式的应用,首先将整个执行的过程划分为若干相同类型的元素,每个元素具备不同的逻辑责任,并将这些元素放到一个栈式的数据结构中,每个元素又有责任负责下一个元素的执行调用。将一个复杂的系统,分而治之,从而将每个部分的逻辑能够高度重用并具备高度扩展性,拦截器在 struts2 中的设计实乃精彩。

实现原理

查看源代码

找到 web.xml 文件中配置拦截器类的位置 org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter,找到这个类中的 execute.executeAction(request, response, mapping) 方法,进入这个方法查看,dispatcher.serviceAction(request, response, mapping),在进入这个方法找到 ActionProxy proxy = getContainer().getInstance(ActionProxyFactory.class).createActionProxy(namespace, name, method, extraContext, true, false) ,这个方法就会创建 action 的代理对象,不是直接 new 出来的,继续往下找到 proxy.execute() 方法,进入方法查看,会发现这是一个接口,所以找到这个方法的实现类 StrutsActionProxy,在这个类中找到 execute() 方法,该方法的返回值是 invocation.invoke(),表示执行 Action 中的方法,进入方法查看也是一个接口,找到这个方法的实现类 DefaultActionInvocation,在这个类的 invoke() 方法中有一个遍历拦截器的操作 if (interceptors.hasNext()),一个拦截器执行完后执行下一个拦截器,最后返回invocation.invoke(),执行 Action 中的方法。

1.4 拦截器和过滤器的区别

  • 过滤器理论上可以过滤任意内容,比如 jsp页面,html页面,servlet,图片路径等等
  • 拦截器只能拦截 Action,每次访问时就会创建一个 Action,多个 Action 的对象。

1.4 自定义拦截器

在 struts2 中有很多默认的拦截器,打开 struts2-core-*.jar 包中的 struts-default.xml 文件,在 <interceptor-stack name="paramsPrepareParamsStack"> 标签中的拦截器就是 struts2 的默认拦截器。在实际的开发中,如果想使用是 struts2 中没有的拦截器功能,这时就要自己写自定义的拦截器。

查看源代码查看拦截器类的结构

在 struts-default.xml 文件中,有很多拦截器,在 <interceptors> 标签中有拦截器的包名,可以找一个进入查看,这些默认的拦截器类都继承自抽象的拦截器类 AbstractInterceptor,抽象拦截器类中有 init(),destroy(),intercept() 三个方法。init() 方法执行拦截器的初始化操作,destroy() 方法执行拦截器的销毁操作,intercept() 方法执行拦截器具体的逻辑操作。但在开发中建议继承 MethodFilterInterceptor 类来实现自定义的拦截器,这样可以对 Action 中的某些方法不进行拦截。

拦截器和 Action 建立关系不是直接在 Action 中调用拦截器的方法,而是通过配置文件的方式让两者建立关系的。

拦截器实现的步骤:

    1. 创建拦截器类,继承 MethodFilterInterceptor 类
    1. 重写 MethodFilterInterceptor 类中的 doIntercept() 方法,在这个方法写拦截器的逻辑

拦截器配置的步骤:

    1. 在要拦截的 action 标签所在的 package 标签中声明拦截器
    1. 在具体的 action 标签中使用声明的自定义拦截器
    1. 手动配置 struts2 的默认拦截器

下面就通过一个用户登陆的案例来说明自定义拦截器的使用。

在 Web 应用中,用户需要在登录之后才能使用主页面的功能,如果用户没有登录,则在使用主页面的功能之前先让其登录,用户登录成功,在 session 中保存用户名。

示例代码如下:

拦截器类

package cc.wenshixin.interceptor;

import javax.servlet.http.HttpServletRequest;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;

public class LoginInteceptor extends MethodFilterInterceptor{

    @Override
    //在这个方法中写拦截器的逻辑
    protected String doIntercept(ActionInvocation invocation) throws Exception {
        //判断 session 中是否有用户名,也即是判断用户是否登录
        HttpServletRequest request = ServletActionContext.getRequest();
        Object obj = request.getSession().getAttribute("username");
        if(obj != null)
        {
            //已经登录,则执行 Action 中的方法
            return invocation.invoke();
        }else
        {
            //未登录,则返回值使其跳转到登录页面
            return "login";
        }
    }
    
}

Action 类

package cc.wenshixin.action;

import javax.servlet.http.HttpServletRequest;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionSupport;

public class ActionDemo extends ActionSupport{
    public String login()
    {
        //获取表单数据
        HttpServletRequest request = ServletActionContext.getRequest();
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        //这里为了简单就直接写死判断了,实际开发中要在数据库查询
        if("admin".equals(username) && "123".equals(password))
        {
            //登录成功,设置session值,并挑转到主页面
            request.getSession().setAttribute("username", username);
            return "success";
        }else
        {
            //用户名密码错误,则跳转回登录页面
            return "login";
        }
    }
    
    public String add()
    {
        //跳转到添加页面
        return "add";
    }
    
    public String delete()
    {
        //跳转到删除页面
        return "delete";
    }
}

struts2 配置文件

注意:如果在配置文件中配置自定义的拦截器,默认的 struts2 拦截器就不会执行了,所以要把默认的拦截器手动使用一下。

<?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>
  <package name="demo" extends="struts-default" namespace="/">
    <!-- 声明拦截器 -->
    <interceptors>
      <interceptor name="loginInteceptor" class="cc.wenshixin.interceptor.LoginInteceptor"></interceptor>
    </interceptors>
    <action name="user-*" class="cc.wenshixin.action.ActionDemo" method="{1}">
      <!-- 使用自定义的拦截器 -->
      <interceptor-ref name="loginInteceptor">
        <!-- 配置不拦截的方法,多个方法用逗号隔开 -->
        <param name="excludeMethods">login</param>
      </interceptor-ref>
      <!-- 手动使用默认的拦截器 -->
      <interceptor-ref name="defaultStack"></interceptor-ref>
      <!-- 设置页面跳转为重定向方式 -->
      <result name="success" type="redirect">/index.jsp</result>
      <result name="login" type="redirect">/login.jsp</result>
      <result name="add" type="redirect">/add.jsp</result>
      <result name="delete" type="redirect">/delete.jsp</result>
    </action>
  </package>
</struts>

jsp 页面

    1. 登录页面
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
  <h1>登录页面</h1>
  <form action="{pageContext.request.contextPath}/user-login.action" method="post">
        用户名:<input type="text" name="username"><br>
        密码:    <input type="password" name="password"><br>
      <input type="submit" value="登录"> 
  </form>
</body>
</html>
    1. 主页页面
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
  <h1>欢迎 ${sessionScope.username} 主页面</h1>
  <a href="{pageContext.request.contextPath}/user-add.action" target="_parent">跳转到添加页面</a>
  <a href="{pageContext.request.contextPath}/user-delete.action" target="_parent">跳转到删除页面</a>
</body>
</html>
    1. 添加页面
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
  <h1>这是添加页面</h1>
</body>
</html>
    1. 删除页面
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
  <h1>这是删除页面</h1>
</body>
</html>

2.struts2 标签库


2.1 普通标签

  • <s:property>:和 OGNL 表达式一起使用在 jsp 页面中获取值栈中的数据
  • <s:iterator>:获取值栈 list 集合的数据,遍历 list 集合中的值
  • <s:debug>:查看值栈结构和数据

2.2 表单标签

2.2.1 HTML 表单相关标签回顾

form 表单

<form action="" method="" enctype="">

输入项

普通输入项: <input type="text">
密码输入项: <input type="password">
单选输入项: <input type="radio">
复选输入项: <input type="checkbox">
文件上传项: <input type="file">
隐藏输入项: <input type="hidden">
普通按钮: <input type="button">
提交按钮: <input type="submit">
图片按钮: <input type="image">
重置按钮: <input type="reset">
下拉输入项: <select><option></select>
文本域: <textarea rows="" cols=""></textarea>

2.2.2 struts2 中对应的表单标签(知道即可)

注意要在 jsp 中引入 struts2 的标签库。

  <s:form>
    <!-- 普通输入项 -->
    <s:textfield name="username" label="用户名"></s:textfield>
    <!-- 密码输入项 -->
    <s:password name="password" label="密码"></s:password>
    <!-- 单选输入项 -->
      <!-- value的值和显示的值一样的写法 -->
    <s:radio list="{'男','女','保密'}" name="sex" label="性别"></s:radio>
      <!-- value的值和显示的值不一样的写法 -->
    <s:radio list="#{'nan':'男','nv':'女','secret':'保密'}" name="sex" label="性别"></s:radio>
    <!-- 复选框输入项 -->
    <s:checkboxlist list="{'看书','写字','画画'}" name="love" label="爱好"></s:checkboxlist>
    <!-- 下拉输入项 -->
    <s:select list="{'北京','上海','广州'}" name="address" label="地址"></s:select>
    <!-- 文件输入项 -->
    <s:file name="file" label="上传文件"></s:file>
    <!-- 隐藏输入项 -->
    <s:hidden name="hidden" value="123"></s:hidden>
    <!-- 文本域 -->
    <s:textarea rows="10" cols="10"></s:textarea>
    <!-- 提交按钮 -->
    <s:submit value="提交"></s:submit>
    <!-- 重置按钮 -->
    <s:reset value="重置"></s:reset>
  </s:form>

页面效果如下图所示

效果图

页面源码如下图所示

查看网页源代码,可以发现,struts2 的 form 标签里面是通过 table 来布局的,这并不符合现在的网页布局方式(Div 布局),也不利于页面的美化,所以 struts2 的 form 标签基本不用。

页面源码
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容

  • 概述 什么是Struts2的框架Struts2是Struts1的下一代产品,是在 struts1和WebWork的...
    inke阅读 2,225评论 0 50
  • action中如何接受页面传过来的参数 第一种情况:(同名参数) 例如:通过页面要把id=1 name=tom a...
    清枫_小天阅读 2,907评论 1 22
  • 1.struts2 框架介绍 struts2 框架在 struts1 和 WebWork的技术上合并而来的,全新的...
    Wizey阅读 470评论 11 3
  • 文/余夏 那年桃李树下, 我们青春年华, 是你们的汗水灌溉, 一笔一划 书写我们三年里的酸甜苦辣。 那年桃李树下,...
    小儒小闹阅读 503评论 0 0
  • 为用户机项目创建自定义内容类型 建立一个新的内容类型 点击“结构”,选择”内容类型“,”添加内容类型“,其名称为文...
    如白驹过隙阅读 189评论 0 0