前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SSH框架之旅-struts2(4)

SSH框架之旅-struts2(4)

作者头像
Wizey
发布2018-08-30 09:41:10
5460
发布2018-08-30 09:41:10
举报
文章被收录于专栏:编程心路

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 中保存用户名。

示例代码如下:

拦截器类

代码语言:javascript
复制
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 类

代码语言:javascript
复制
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 拦截器就不会执行了,所以要把默认的拦截器手动使用一下。

代码语言: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>
  <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. 登录页面
代码语言:javascript
复制
<%@ 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. 主页页面
代码语言:javascript
复制
<%@ 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. 添加页面
代码语言:javascript
复制
<%@ 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. 删除页面
代码语言:javascript
复制
<%@ 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 的标签库。

代码语言:javascript
复制
  <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 标签基本不用。

页面源码

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017.10.04 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.Struts2 拦截器
    • 1.1 AOP 思想
      • 1.2 拦截器概述
        • 1.3 struts2 拦截器原理
          • 1.4 拦截器和过滤器的区别
            • 1.4 自定义拦截器
            • 2.struts2 标签库
              • 2.1 普通标签
                • 2.2 表单标签
                  • 2.2.1 HTML 表单相关标签回顾
                  • 2.2.2 struts2 中对应的表单标签(知道即可)
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档