Struts2.0第二章(封装数据获取请求参数、servlet api的获取、ServletActionContext、注入、ognl、valueStack、ActionContext、interceptor拦截器)

Struts2框架封装数据:(在action中如何获取请求参数)

两种方式:
  1.属性驱动
    a.直接在action类中提供与请求参数名称匹配的成员属性,并提供get/set方法;
    b.创建一个javabean模型,在javabean中提供与请求参数名称匹配的成员属性,并提供get/set方法;
      在action类中提供类型为该javabean成员变量n,并提供该javabean的get/set方法。。
      页面中的参数名称为:n.成员变量
      eg:user.username,user.password,要使用这种ognl表达式。
    以上两种方式的优缺点:
      第一种比较简单,在实际操作中需要将action属性再赋值给javabean来封装数据来传递操作,会很麻烦;
      第二种:不需要再次封装值到javabean,但是它要求页面上把必须使用ognl表达式,存在页面不通用问题;
  2.模型驱动
    步骤:
      1.让Action类实现一个接口ModeDriven<JavaBean>
      2.在Action类中实例化模型对象(就是要new出来javabean)
      3.重写getModel方法将实例化的模型返回。
        eg:

1 public class LoginAction implements ModelDriven<User>{
2   private User user = new User();
3   @Override
4   public User getModel(){
5     return user;
6   }
7 }

    对于模型驱动它与属性驱动对比,在实际开发中使用比较多,模型驱动缺点,它只能对一个模型数据进行封装。

Struts2.0Action中获取servlet api

  方法一:ServletActionContext获取    

static ActionContext getActionContext(HttpServletRequest request)
static ActionMapping getActionMapping()
static PageContext getPageContext()
static HttpServletRequest getRequest()
static HttpServletResponse getResponse()
static ServletContext getServletContext()
static ValueStack getValueStack(HttpServletRequest request)
static void setRequest(HttpServletRequest request)
static void setResponse(HttpServletResponse response)
static void setServletContext(ServletContext servletContext)

eg:

       HttpServletRequest request = ServletActionContext.getRequest();
            HttpSession session = request.getSession();
            ServletContext servletContext = ServletActionContext.getServletContext();
            String username = request.getParameter("username");
            String password = request.getParameter("password");

  方法二:采用注入的方式

     分析:在action执行之前会走一些默认的拦截器,其中有一个名为servletConfig的拦截器,这个拦截器会判断你当前的action是否实现了指定的接口,如果实现了他就帮你把需要的对象注入到当前的action中。

      实现的接口如下:

ServletRequestAware
ServletResponseAware
ServletContextAware
。。。。。。

eg:

 1     public class LoginApi implements ServletRequestAware{
 2             private HttpServletRequest request;
 3             public void login() {
 4                 String username = request.getParameter("username");
 5                 System.out.println(username);
 6             }
 7             @Override
 8             public void setServletRequest(HttpServletRequest request) {
 9                 this.request = request;
10             }
11        }

OGNL表达式

概述:
  object-graph navigation language(对象图导航语言),它是一种功能强大的表达式语言,通过它简单一致的表达式语言,可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能。
  ognl类似于el,但是比el更加强大
  struts2框架内置了ognl表达式
  ognl本身就是一个项目,它是可以单独使用的。

ognl作用:
  1.支持对象的操作,调用对象的方法
  2.支持静态成员访问
  3.支持赋值操作与表达串联
  4.访问ognl上下文,访问ActionContext
  5.操作集合对象
ognl三要素:
  表达式
  OgnlContext 上下文
  Root 根

单独使用Ognl示例:

 1 package test;
 2 
 3 import java.util.HashMap;
 4 import java.util.Map;
 5 
 6 import org.junit.Test;
 7 
 8 import ognl.Ognl;
 9 import ognl.OgnlContext;
10 import ognl.OgnlException;
11 
12 public class OgnlTest {
13     @Test
14     public void test() throws OgnlException {
15         //获取上下文对象OgnlContext
16         OgnlContext context = new OgnlContext();//OgnlContext implements java.util.Map
17         Object root = context.getRoot();//获得根root,初始值为null,需要设置root根值
18         
19         //数据操作(取值)
20         //示例1:支持对象的访问
21         Object value = Ognl.getValue("'hello'.length()",context,root);//传入表达式、OgnlContext上下文对象context、根
22         System.out.println(value);
23         
24         //示例2:支持静态成员的访问(语法格式    @类全名@成员变量/成员方法)
25         Object MathRandom = Ognl.getValue("@java.lang.Math@random()",context,root);
26         Object mathPI = Ognl.getValue("@java.lang.Math@PI",context,root);
27         System.out.println(MathRandom);
28         System.out.println(mathPI);
29         
30         //示例3:访问Ognl上下文 (语法格式    在键名称前加#号)
31         context.put("username", "张三丰");//向上下文中存入数据
32         Object contextValue = Ognl.getValue("#username",context,root);
33         System.out.println(contextValue);
34         
35         //示例4:访问root根值(语法格式    当root根值中存入的是Map集合时,直接写需要取的键名;当root根值中存入的是List集合时,直接写索引即可。eg:"[index]")
36         Map<String, String> map = new HashMap<String,String>();
37         map.put("username", "张无忌");
38         context.setRoot(map);
39         Object rootValue = Ognl.getValue("username", context, context.getRoot());
40         System.out.println(rootValue+"...");
41         
42         //示例5:操作集合
43         Object arrayList = Ognl.getValue("{1,2,3,4,5}", context, context.getRoot());//相当于创建了一个list集合
44         System.out.println(arrayList.getClass());
45         Object linkedHashMap = Ognl.getValue("#{'username':'lisi','password':'123','age':18}",context,rootValue);//相当于创建了一个map集合
46         System.out.println(linkedHashMap.getClass());
47         
48         //示例6:支持表达式赋值(对表达式的值进行重新赋值)
49         Object value2 = Ognl.getValue("#username",context,rootValue);
50         Object exitsValue2 = Ognl.getValue("#username='老王'",context,rootValue);
51         System.out.print(value2+"->");
52         System.out.println(exitsValue2);
53         
54         //示例7:支持表达式串联(串联就是当有多个表达式存在时,执行最后一个表达式的值)
55         Object value3 = Ognl.getValue("#username",context,rootValue);
56         Object exitsValue3 = Ognl.getValue("#username,'hello'",context,rootValue);
57         System.out.print(value3+"->");
58         System.out.println(exitsValue3);
59     }
60 }
单独使用Ognl示例

struts2.0框架中使用Ognl表达式

  在struts2.0框架中我们使用ognl表达式从valueStack中获取数据
  在struts2.0框架中我们可以使用ognl+valueStack达到在页面(jsp)上来获取相关的数据;
    要想在jsp页面上使用ognl表达式,就需要结合struts2框架的标签<s:property value = "ognl表达式"/>来使用;
使用步骤:
  在jsp中引入标签库:<%@ taglib uri="/struts-tags" prefix="s"%>
  编写ognl表达式:eg:<s:property value = "'hello'.length()"/>

valueStack 值栈

  valueStack主要目的就是为action中产生的数据携带到页面上,也就是说valueStack就是一个容器。
  在struts2框架中valueStack被设计成一个接口com.opensymphony.xwork2.util.ValueStack,我们主要使用的是它的实现类com.opensymphony.xwork2.ognl.OgnlValueStack.
  当客户端向我们发送一个请求,服务器就会创始一个Action来处理请求,struts2中的action是一个多例的,每一次请求都会有一个新的action对应,所以它不存在线程安全的问题,一个valueStack对应一个action,valueStack贯穿整个action的生命周期。struts2框架将valueStack保存在request中。
valuleStack 内部结构
valueStack主要有两部分组成:
  valueStack实现类OgnlValueStack中主要有成员变量CompoundRoot root;Map<String,Object> context;
    CompoundRoot root : 它就是一个ArrayList,它主要存贮action的相关数据,用户手动存入的数据;
    Map<String,Object> context : 就是一个Map,context中主要存储了关于web开发中常见对象;
      eg:
        parameters:请求参数
        request:请求对象中所有属性
        session:会话对象中所有属性
        application:application对象中的所有发展
        。。。。。。
        context中其实存储都是都是这些对象的Map信息。
在struts2框架中我们通过ognl表达式来获取valueStack中的数据:
  没有使用#就会从CompoundRoot root中获取数据;
  如果使用#就会从Map<String,Object> context 中来获取数据;

ValueStack获取:

  方式一:直接通过reqeust对象来获取
    ValueStack valueStack = (ValueStack)ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
  方式二:使用ActionContext来获取
    ValueStack valueStack = ActionContext.getContext().getValueStack();

ActionContext的介绍:

ActionContext它是action上下文,struts2框架使用ActionContext来保存Action在执行过程中所需要的一些对象,eg:session,application。。。
ActionContext是跟本地线程所绑定的。如果要获取直接可以通过如下代码获取到。
ActionContext context = ActionContext.getContext();
struts2会根据每一次的http请求来创建对应的ActionContext,它是与当前县城绑定的。每一次请求,就是一个线程,对应着一个request,Action,ActionContext,ValueStack。

ServletActionContext extends ActionContext

ValueStack操作-存储数据

注意:我们使用valueStack来存储数据时主要是向root中存储;
手动存储:(push&set都是向root中来存储)
  push(Object); //向root中存储一个Object类型的数据;
  set(key,Object);//向root中存入一个Map集合,底层会先从root中找第一个元素即下标为0的元素,看它是否是Map,当它是Map,并且该Map中可以检索到key=OgnlValueStack.MAP_IDENTIFIER_KEY的值则返回该map然后向该map中存入数据;否则会创建一个HashMap,然后存入        key=OgnlValueStack.MAP_IDENTIFIER_KEY,value=""的一个值,然后向该map中存入数据。
  push()/set()底层都是调用的ArrayList中add(index,Object)方法,且每次add都是向索引为0的位置插入数据;所以该root中的数据结构为栈结构,先进后出。
    eg:ValueStack valueStack = ActionContext.getContext().getValueStack();
      valueStack.push();
      valueStack.set();
struts2.0框架自动向valueStack->root中存储数据:
  每次请求,访问action,action这个对象会被自动push()存储到valueStack中;
  当action实现了ModelDriven接口时,会将模型对象自动push()存储到到valueStack中;

ValueStack操作-获取数据

  <s:debug/> 在jsp页面中可以使用此标签进行调试,先要将strusts的jar包导入方可使用。
  在客户端页面中该标签被解析为一个超链接,点击开可以看到valueStack值栈,分为两部分,上面为root,下面为context;(root中struts会自动存入action对象)
  当取root中Map集合里面的值时,jsp页面ognl表达式只需要写map中对应key的值方可取出对应的value值;
  当取root中其他元素的值时,需要使用下标来取,当你取某一个下标的值时,会将root中以这个下标为起始后的所有值都取到,所以需要加上top,取第一个值即可。eg:<s:property value="[1].top"/>
  当取root中action对象中的值时,其实底层是调用的action的getter方法得到getter方法的返回值;直接写action中成员变量(其实是get方法名称去掉get首字母小写)的名称就可以获得到该值。
  根据root取action对象的语法规则,是否root中存入action对象的值是将所有的值封装到一个map然后存入到root中?
  当action继承了ModelDriver,需要取得对象模型驱动的值时,直接用该对象属性名称取值即可;
  当该action的逻辑方法中对模型对象重复赋值后,要取得赋值后的新值时,需要使用model.对象属性名取值。(底层是使用getModel()方法)
  (从action中取得模型对象的值,思考,是否取对象的属性值亦是直接用该对象属性名称取值即可)

EL表达式可以从valueStack中获取数据

  因为struts2对request做出了增强操作,重写了getAttribute()方法,当el表达式request域对象中取不到值就会取valueStack中取值。
Ognl表达式中的特殊符号
ognl是通常要结合struts2的标识一起使用,主要是#、%、$这三个符号的使用:
  #号:它是从context中获取数据;
    eg:向域对象request中存入名为username的zhangsan值,因为web开发中常用的对象都是存储在valueStack->context中,那么在ognl中获取:<s:property value="#request.username"/>
      还可以通过parameters对象获得到前端传来的参数:<s:property value="#parameters.username"/>
  %:用于是否强制解析ognl表达式
    eg:
      <s:property value="%{#request.username}"/> 会解析ognl
      <s:property value="%{'#request.username'}"/> 不会解析ognl
  $: 主要是从配置文件中来获取valueStack中数据
    当跳转方式为重定向的时候,我们还想要将上一个页面的valueStack中的值带入到重定向之后的页面,那么可以将需要的值拼接到跳转路径的后面:

    eg:
      <action>
        <!-- 可以将上个页面valueStack的值通过${表达式}方式获取拼接到跳转页面后面 -->
        <result name = "" type = "redirect">/ognl.jsp?username=${username}</result>
      </action>

关于登陆失败的处理:

  在用户登陆时,用户名或密码错误,将自定义的错误信息存储到valueStack中,在登陆页面通过ognl表达式获取错误信息展示:
  其实在实际开发时,我们一般会让action取继承ActionSupport类,那么就是使用父类提供的对错误操作的处理:
    this.addActionError("用户名或密码错误"); //用于对用户名密码的逻辑判断
  在页面上可以直接使用struts2提供的标签来显示错误信息:
    <s:actionerror/>
  类似的还有:
    this.addFieldError(fieldName,errorMessage); ---> <s:fielderror/> //用于对用户名和密码的校验(长度/数字开头。。。)
    this.addActionMessage(aMessage); ---> <s:actionmessage/> //用于显示普通的消息

关于展示数据:

方案一:
  将需要展示的数据封装到多个javabean中,将多个javabean封装到一个List中,将该List存入valueStack->root中,在前端页面通过struts的<s:iterator>标签来获取:
  <s:iterator value="ognl表达式" var="变量名">
    <tr>
      <td><s:property value="#变量名.beanAttributeName"/></td>
    </tr>
  </s:iterator>
  注意:如果使用var,它存储在context中,所以在使用时要添加#号,也可以如下方案:
  <s:iterator value="ognl表达式">
    <tr>
      <td><s:property value="beanAttributeName"/></td>
    </tr>
  </s:iterator>
  说明:上面写法就表示直接在ognl表达式中取到的对象直接遍历该对象属性值
方案二:
  我们在action中声明一个List集合属性,并且提供getter/setter方法,在逻辑方法中给该list成员赋值,因为valueStack->root中会自动存储action的相关属性,所以就可以直接在页面上取得该list的属性值。

Interceptor拦截器介绍:

struts2.0中Interceptor它基于spring aop思想,而aop思想本质时通过动态代理来实现,我们的struts2拦截器主要时拦截action操作,在action执行前后进行一些其他功能操作。

  (通过对action产生动态代理对象,对代理对象的方法增强,在执行代理对象的方法之前会执行定义的所有拦截器栈,拦截器栈在这里是采用递归调用的模式)
  (Filter过滤器是拦截请求的,Interceptor拦截器是拦截action执行的)
  拦截器链(拦截器栈)简单说,就是可以将多个拦截器形成一个链,在访问他们时依次访问。
struts2.0的执行流程图:

自定义Interceptor拦截器介绍:

使用interceptor可以在action执行指定方法前后进行处理工作,例如,完成权限控制。
如何定义Interceptor?
  1.所有Interceptor都要实现接口com.opensymphony.xwork2.interceptor.Interceptor,重写intercept()方法(需要实现的功能);
  2.在配置文件struts.xml中的<package>标签下配置自定义的Interceptor。
    <interceptors>
      <interceptor name="interceptor1" class=""></interceptor>
      <interceptor name="interceptor2" class=""></interceptor>
      <interceptor-stack name="mystack">
        <interceptor-ref name="interceptor1"/>
        <interceptor-ref name="interceptor2"/>
      </interceptor-stack>
    </interceptors>
  3.在action中使用Interceptor
在struts.xml中的<action>中直接配置拦截器或者拦截器栈
<interceptor-ref name="[interceptor1/mystack]"></interceptor-ref>
注意:当我们显示的引入一个自定义的Interceptor,那么默认的defaultStack就不会在导入,需要手动导入。
eg:
  <action>
    <interceptor-ref name = "mystack"/>
    <!-- 手动导入defaultStack -->
    <interceptor-ref name = "defaultStack"/>
  </action>
  又或者直接在自定义的mystack栈中引入defaultStack,只需要在action中引入mystack即可。

Interceptor练习:

  @Override
  public String intercept(ActionInvocation invocation)throw Exception{
    invocation.getAction(); //得到当前访问的action,有了action对象可以向前台传递信息(addActtionError,addFieldError,addActionMessage...)
    return invocation invoke(); // 表示放行
    return "viewPath"; //表示转发到该action的viewPath视图
  }
注意:我们在struts.xml文件中配置action时候,可以使用*通配符,这是它可以处理多个方法,你指定的interceptor只想拦截某一个方法,该如何处理?
我们可以使用Interceptor接口的一个实现类(MethodFilterInterceptor)来完成操作逻辑操作同上,只是需要在struts.xml文件中声明该拦截器时传入指定的参数,指示需要拦截的方法,或者不需要拦截的方法即可。
  eg:
    <interceptors>
      <interceptor name = "" class = "">
      <!-- 以下两种方式选择一种即可 -->
      <param name = "excludeMethods"></param> //包含的方法
      <param name = "includeMethods"></param> //不包含的方法
    </interceptors>

原文地址:https://www.cnblogs.com/laodang/p/9551166.html