Struts2 快速笔记2

10 篇文章 0 订阅

路线:

  1. Struts2的概述、Struts2的入门、Struts2常见的配置、Struts2的Action的编写
  2. Struts2的数据的封装、结果页面配置
  3. Struts2的值栈和OGNL表达式
  4. Struts2的标签库

上一篇:Struts2 快速笔记 1

1. OGNL表达式
  1. OGNL:Object Graph Navigation Language,功能强大的表达式语言——比EL表达式,对象导航语言表达式。

    • EL:只能从域对象中获取数据,11个域对象——request/session/pagecontext等:${name},${request}
    • OGNL:可以调用对象的方法,域对象数据,以及Struts2的值栈的数据。OGNL是独立的第三方语言,Struts将其引用到了,单独也可以使用。
  2. OGNL的功能

    • 支持运算符操作
    • struts2中支持对象方法的调用
    • Struts2中支持静态方法和值的引用
    • 支持赋值和表达式串联
    • 访问OGNL上下文OGNLContext和ActionContext——与值栈有关
    • 操作集合对象,或者new新的对象
  3. 使用3要素:Java中使用,不仅仅在struts2

    • 表达式:从哪里获取什么数据
    • 根对象:操作目标
    • Context对象:在哪里操作,操作环境

    示例(Java中):

    • 包:
      1546600883034

    • Demo

      @Test
      /**
       * OGNL调用对象方法
       */
      public void demo1() throws OgnlException{
          //3要素  context root source
          OgnlContext ognlContext = new OgnlContext();
          Object root = ognlContext.getRoot();
          Object obj = Ognl.getValue("'helloworld'.length()", ognlContext, root);
          System.out.println(obj);
      }
      @Test
      /**
       * OGNL调用静态方法和值:
       * @对象全路径名@方法(参数)
       * @对象全路径名@变量名
       */
      public void demo2() throws OgnlException{
          //3要素  context root source
          OgnlContext ognlContext = new OgnlContext();
          Object root = ognlContext.getRoot();
          Object obj = Ognl.getValue("@java.lang.Math@random()", ognlContext, root);
          Object obj2 = Ognl.getValue("@java.lang.Math@PI", ognlContext, root);
          System.out.println(obj);
      }
      @Test
      /**
       * Context中的数据需要加#
       * Root对象中的数据不需要
       */
      public void demo3() throws OgnlException{
          User user = new User("Tom", "123");
          OgnlContext ognlContext = new OgnlContext();
          //1.Context中
      /*		ognlContext.put("user", user);		
          Object obj = Ognl.getValue("#user", ognlContext, ognlContext.getRoot());
          System.out.println(obj);*/
          //2.Root中,访问的是对象的属性
          ognlContext.setRoot(user);
          Object obj2 = Ognl.getValue("username", ognlContext, ognlContext.getRoot());
          System.out.println(obj2);
      }
      
  4. OGNL在Struts2中使用:主要是从JSP页面的数据交互

    • 页面引入标签:<%@ taglib uri="/struts-tags" prefix="s" %>

    • 获取对象数据

      <h1>Struts2的OGNL表达式</h1>
      <h3>1.访问对象方法</h3>
      <s:property value="'hello'.length()"/>
      <br/>
      <h3>2.访问静态方法-----struts2 默认将其关闭,如@java.lang.Math@random()-------可以在struts的核心包中default.properties中配置常量struts.ognl.allowStaticMethodAccess=true</h3>
      <s:property value="@java.lang.Math@random()"/>
      <br/>
      <h3>3.访问静态成员-----struts2 可以如@java.lang.Math@PI</h3>
      <s:property value="@java.lang.Math@PI"/>
      
    • OGNL其最主要的作用——值栈

2. 值栈空间
  1. 概念

    ValueStack,是一个容器(栈),有struts框架创建。当前端页面如JSP发送一个请求时,Struts的默认拦截器就会将请求中的数据进行封装,然后将其放入ValueStack的栈顶。

    1546604465917

    前端的请求将会被封装成对象,放入值栈容器中。在使用Struts2框架的开发中,一般前端数据的中转都由值栈来进行,尽量较少的使用域对象(request,pageContext等),但也要记住struts中也能使用这些对象。有一个Action的实例,就会有一个值栈的对象,Action是多例的。

    **作用:**存入ValueStack中的数据,在struts2中任何位置能获取到,如页面、配置文件、Action中;而存入域对象如Request对象的数据,只能在JSP页面中获取。

  2. 内部结构

    1546605129641

    值栈中的两个主要区域:root和context。root区继承了一个ArrayList,context区来自OGNL的包,其实现了一个Map接口,包含了一些常见的Web对象的引用。

    • root区一般放置常见的对象,ArrayList——CompoundRoot对象;获取root数据不需要加#

    • context放置对象的引用,Map——OgnlContext对象;获取context数据需要加#

      • root引用
      • request
      • parameters
      • session
      • application
      • attr:小范围的引用,使用它按序依次访问request、session、application中的属性,找到即停。
    • 一般而言,操作的值栈是指的操作Root区

      1546606294076

    • 也可以在JSP的debug中看到<h1>OGNL的值栈内部结构debug</h1><s:debug />

      1546607701397

  3. 值栈与ActionContext

    和ServletContext上下文对象一样,ActionContext对应Action的生命周期。因Struts核心过滤器StrutsPrepareAndExecuteFilterinit方法中会执行加载配置文件(xml中继承自struts-default的,又会执行许多默认的拦截器,实现参数的校验,类型转化和封装等功能)。

    ActionContext:请求产生------StrutsPrepareAndExecuteFilter------执行doFilter方法-----创建ActionContext-----再由其创建ValueStack,并封装到ActionContext中;所有可以使用ActionContext获取值栈对象。

    ActionContext有值栈的引用,于是能够访问Servlet中的request等域对象,就获得其API的相关数据。ActionContext中的获得的上下文对象,其实是通过当前线程来获得的:

    public class ActionContext implements Serializable {
    
        static ThreadLocal<ActionContext> actionContext = new ThreadLocal<ActionContext>();
        /**
         * Constant for the name of the action being executed.
         */
        public static final String ACTION_NAME = "com.opensymphony.xwork2.ActionContext.name";
    
        /**
         * Constant for the {@link com.opensymphony.xwork2.util.ValueStack OGNL value stack}.
         */
        public static final String VALUE_STACK = ValueStack.VALUE_STACK;
    
        /**
         * Constant for the action's session.
         */
        public static final String SESSION = "com.opensymphony.xwork2.ActionContext.session";
        ......
         /**
         * Returns the ActionContext specific to the current thread.
         *
         * @return the ActionContext for the current thread, is never <tt>null</tt>.
         */
        public static ActionContext getContext() {
            return actionContext.get();
        }
    
  4. 获得值栈

    1. 通过ActionContext获取
    2. 在Struts2中也在request中存入了一个值栈引用
    //方式1.
    ValueStack valueStack1 = ActionContext.getContext().getValueStack();
    // 方式2.
    ValueStack valueStack2 = (ValueStack) ServletActionContext.getRequest()
            .getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
    // 二者属于同一Action,ValueStack当然是同一个对象
    System.out.println(valueStack1 == valueStack2);
    return NONE;
    
  5. 操作值栈:操作数据

    1. 通过Action提供了Get方法的属性,获取其属性值。

      • 原因:默认情况下,ValueStack会将Action对象本身也压入栈(Root区)中,所有有get方法的属性都可获取
      public class DoValueStack extends ActionSupport {
      	private User user;
      	//方式1必须要有get属性
      	public User getUser() {
      		return user;
      	}
      	@Override
      	public String execute() throws Exception {
      		//方式1.在action中提供get方法
      		user = new User("Helo", "1234");		
      		return SUCCESS;
      	}
      }
      -----------------------JSP----------------------------------
      <h2>操作值栈方式一,get</h1>
      <s:property value="user.username"/>
      <s:property value="user.password"/>
      
    2. 通过ValueStack自身的API:更常用,不需要太多的Get方法,代码更可读

      @Override
      public String execute() throws Exception {	
          //方式2.使用ValueStack本身的API
          ValueStack valueStack = ActionContext.getContext().getValueStack();
          //添加数据,push(对象),set(key,value)
          User user = new User("ooooo", "4444");
          valueStack.push(user);  //push的对象保存在栈顶位置!!!!!!!!!!!!!!!!
          valueStack.set("aaa", "123456");
          return SUCCESS;
      }
      ----------------------------JSP---------------------------------
      <s:property value="username"/>     ooooo
      <s:property value="aaa"/>      123456
      

      1546611744719

  6. 获取值栈数据

    1. 获取Root区中的数据——获取一个对象或者集合

      public String execute() throws Exception {	
      		//
      		ValueStack valueStack = ActionContext.getContext().getValueStack();
      		//1个对象
      		User user = new User("张三", "123456");
      		valueStack.push(user);		
      		List<User> list = new ArrayList<>();
      		list.add(new User("李四", "123"));
      		list.add(new User("王五", "123"));
      		list.add(new User("赵六", "123"));
      		valueStack.set("list", list);
      		return SUCCESS;
      	}
      ----------------------------JSP--------------------------
      <h2>1.获取Root区的数据 单个对象</h1>
      <s:property value="username"/>
      <s:property value="password"/>
      <h2>2.获取Root区的数据 集合</h1>
      <s:property value="list[0].username"/>
      <s:property value="list[0].password"/><br/>
      <s:property value="list[1].username"/>
      <s:property value="list[1].password"/><br />
      <s:property value="list[2].username"/>
      <s:property value="list[2].password"/>
      
    2. 获取context区中的数据(不常用)

      ServletActionContext.getRequest().setAttribute("myname", "孙七");
      ServletActionContext.getRequest().getSession().setAttribute("myname", "朱八");
      ServletActionContext.getServletContext().setAttribute("myname", "周九");
      ------------------------------JSP---------------------------
      <h2>3.获取context区的数据 </h1>
      <s:property value="#request.myname"/>
      <s:property value="#session.myname"/>
      <s:property value="#application.myname"/>
      <s:property value="#attr.myname"/>
      <h2>4.获取请求字符串参数 </h1>
      <s:property value="#parameters.id"/>
      
  7. EL为何能访问值栈数据

    源码:Struts中初始化时对request进行了增强包装(request.getAttribute),框架将其进行了改造。使用EL表达式时,先会访问原request等域对象中数据,没有获取到时则进入ActionContext中,进入值栈进行查找。

    于是,EL表达式同样能获取到值栈数据。

    1546700629166

 

3. OGNL 中的特殊字符
  • <% request.setAttribute("name", "张三"); %>
    <h2>OGNL中#号的使用</h2>
    <h3>1.获取context域中的数据</h3>
    <s:property value="#request.name"/>
    <h3>2.构造集合list</h3>
    <s:iterator var="i" value="{'aa','bb','bb'}">
    	<s:property value="i"/>------<s:property value="#i"/><br/>
    </s:iterator>
    
    <h3>2.构造集合map1</h3>
    <s:iterator value="#{'aa':'11','bb':'22','cc':'33'}">
    	<s:property value="key"/>------<s:property value="value"/><br/>
    </s:iterator>
    
    <h3>2.构造集合map2, 使用了var就必须加#号,从context区中获取</h3>
    <s:iterator var="entry" value="#{'aa':'11','bb':'22','cc':'33'}">
    	<s:property value="#entry.key"/>------<s:property value="#entry.value"/><br/>
    </s:iterator>
    
    <h3>3.#号在一些常用表单中使用,构建集合</h3>
    性别:<input type="radio" value="1" name="sex1"><input type="radio" value="2" name="sex1"><br>
    <h4>3.1使用list</h4>
    <s:radio list="{'',''}" name="sex2" label="性别"></s:radio>
    <h4>3.2使用map</h4>
    <s:radio list="#{'1':'','2':''}" name="sex3" label="性别"></s:radio>
    
  • %

    常用于在HTML标签中,显示获取数据(也可以直接嵌套,但在OGNL的标签中无法嵌套);也就是强制解析OGNL语句,同样也可以强制失效OGNL

    <s:textField name="name" value="%{#request.name}"/>
    <input type="text" name="name2" value="<s:propery value='#request.name'>"> //可嵌套
    <s:property value="%{‘#request.name’}"/>
    
  • $

    一般在配置文件xml或properties中使用,来使用OGNL获得数据。

    • 属性文件:使用$来限定OGNL表达式,否则会被视为字符串,如${#session.user.username}
    • xml:同上使用
案例:查询优化,将查询的数据存入值栈中,在页面使用OGNL获取。

 

4. Struts拦截器
  1. interceptor,作用时拦截(过滤)Action;相比Filter过滤的是客户端向服务器发送的请求,而拦截器拦截的是客户端对Action的访问,粒度更细,可以拦截到Action的具体方法。拦截器可以用来做一些权限,控制之类的功能。

    Struts2框架核心的功能都是依赖拦截器实现

  2. Struts的执行流程

    1546751132551

    客户端向服务器发送一个Action的请求,执行核心过滤器(doFilter)方法。在这个方法中,调用executeAction()方法,在这个方法内部调用dispatcher.serviceAction();在这个方法内部创建一个Action代理,最终执行的是Action代理中的execute(),在代理中执行的execute方法中调用ActionInvocation的invoke方法。在这个方法内部递归执行一组拦截器(完成部分功能),如果没有下一个拦截器,就会执行目标Action,根据Action的返回的结果进行页面跳转。

    1546751078859

     

    1. 自定义拦截器:编写一个类实现Interceptor接口或者继承AbstractInterceptor类;同样,在设置默写权限的时候,可以继承MethodFilterInterceptor,来完成更小粒度(基于方法)的控制和过滤。

      public class InterceptorDemo3 extends AbstractInterceptor {
      	@Override
      	public String intercept(ActionInvocation invocation) throws Exception {
      		System.out.println("intercepto3 EXE。。。。");
      		//递归执行其他的拦截器
      		String obj = invocation.invoke();
      		System.out.println("intercepto3 Done。。。。");
      		return obj;
      	}
      }
      

      配置拦截器:

      <package name="demo1" extends="struts-default" namespace="/">
          <!-- 配置拦截器 -->
          <interceptors>
              <interceptor name="interceptorDemo1" class="com.leehao.strutslearning.interceptor.InterceptorDemo1" />
              <interceptor name="interceptorDemo2" class="com.leehao.strutslearning.interceptor.InterceptorDemo2" />
              <interceptor name="interceptorDemo3" class="com.leehao.strutslearning.interceptor.InterceptorDemo3" />
          </interceptors>	
          <action name="actionDemo1" class="com.leehao.strutslearning.action.ActionDemo1">
              <result>/demo1.jsp</result>
              <!-- 将拦截器引入栈,注:一旦添加了自定义的拦截器,则必须要手动选择是否执行默认的那一堆拦截器 -->
              <interceptor-ref name="defaultStack"></interceptor-ref>
              <interceptor-ref name="interceptorDemo1"></interceptor-ref>
              <interceptor-ref name="interceptorDemo2"></interceptor-ref>
              <interceptor-ref name="interceptorDemo3"></interceptor-ref>
          </action>		
      </package>
      

      方法二:

      <package name="demo1" extends="struts-default" namespace="/">
          <!-- 配置拦截器 -->
          <interceptors>
              <interceptor name="interceptorDemo1" class="com.leehao.strutslearning.interceptor.InterceptorDemo1" />
              <interceptor name="interceptorDemo2" class="com.leehao.strutslearning.interceptor.InterceptorDemo2" />
              <interceptor name="interceptorDemo3" class="com.leehao.strutslearning.interceptor.InterceptorDemo3" />
              <interceptor-stack name="myStack">
                  <interceptor-ref name="defaultStack"></interceptor-ref>
                  <interceptor-ref name="interceptorDemo1"></interceptor-ref>
                  <interceptor-ref name="interceptorDemo2"></interceptor-ref>
                  <interceptor-ref name="interceptorDemo3"></interceptor-ref>
              </interceptor-stack>
          </interceptors>	
          <action name="actionDemo1" class="com.leehao.strutslearning.action.ActionDemo1">
              <result>/demo1.jsp</result>
              <interceptor-ref name="myStack"></interceptor-ref>			
          </action>		
      </package>
      
  3. 注意点

    • 使用拦截器是在各个action中配置的,建议使用method粒度的拦截器;
    • 拦截器主要针对不同的action,页面如JSP的访问使用过滤器来进行控制
    • 拦截器可以分别在action中配置,也可以设置为拦截器的栈,按名称引用;
    • 一定记得同时配置默认的拦截器栈——defaultStack

 

5. Struts 的标签

struts有自己的标签,主要的好处是方便数据的记录和回显。分为通用标签(语句)和UI标签(表单等)。

  1. 通用标签——表达式

    1546775993162

    最常用:

    • 判断标签if
    • 迭代标签iterator
    • property
    • debug
    • date
  2. UI标签——常用作数据回显,且有自己的样式,较为灵活。可以在struts.properties中设置属性,也可以对单个的标签进行theme=simple的设置,取消默认为xhtml的样式。

    1546776468104

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值