zoukankan      html  css  js  c++  java
  • struts2 18拦截器详解(七)

    ChainingInterceptor


       该拦截器处于defaultStack第六的位置,其主要功能是复制值栈(ValueStack)中的所有对象的所有属性到当前正在执行的Action中,如果说ValueStack中没有任何对象的话,该拦截器不会干任何事情,看到这个拦截器的名称,大家应该会想到有一种chain类型的Result,该拦截器主要就是针对chain类型Result起作用的,因为我们有可能在chain链后面的Action用到前面Action的属性,所以struts2提供了该拦截器来实现这个功能。当然我们也可以让chain链中的某个Action属性不复制到正在执行的Action中,只要chain链中的Action实现com.opensymphony.xwork2.Unchainable接口,这个Action中的属性就不会复制到当前正在执行的Action中了。下面我们看一个该拦截器的源码:

    @Override
    public String intercept(ActionInvocation invocation) throws Exception {
        ValueStack stack = invocation.getStack();//获取值栈
        CompoundRoot root = stack.getRoot();//获取值栈中的root对象,其实就是一个List
    	//如果值栈中有对象
        if (root.size() > 1) {
        	//把值栈中的对象引用复制到一个List中,目的是不破坏值栈中的对象
            List<CompoundRoot> list = new ArrayList<CompoundRoot>(root);
            list.remove(0);//移除第一个元素,即当前正在执行的Action对象
            //然后将该List中对象反序,这样复制到当前Action中的属性是chain链中最后面Action的属性
            Collections.reverse(list);
    		//获取ActionContext对象内部的contextMap对象,其实就是OgnlContext对象
            Map<String, Object> ctxMap = invocation.getInvocationContext().getContextMap();
            Iterator<CompoundRoot> iterator = list.iterator();
            int index = 1; // starts with 1, 0 has been removed
            while (iterator.hasNext()) {//迭代
            	index = index + 1;
                Object o = iterator.next();
                if (o != null) {
                	if (!(o instanceof Unchainable)) {//如果当前被迭代的对象没有实现Unchainable接口,则要进行属性复制
                		reflectionProvider.copy(o, invocation.getAction(), ctxMap, excludes, includes);//复制属性
                	}
                }
                else {
                	LOG.warn("compound root element at index "+index+" is null");
                }
            }
        }
        
        return invocation.invoke();
    }


    注释已经写得清楚了,下面以一段Action配置来进一步讲解,配置如下:

    <action name="test" class="com.xtayfjpk.action.TestAction">
    	<result name="chain" type="chain">chain</result>
    </action>
    <action name="chain" class="com.xtayfjpk.action.ChainAction">
    	<result name="success">/WEB-INF/page/message.jsp</result>
    </action>


       这里test action的Result类型为chain,指向了chain action,当执行TestAction时,值栈中就两个对象,第一个是TestAction自己,第二个是一个com.opensymphony.xwork2.DefaultTextProvider对象,由于当前Action被移除不会参数迭代,而DefaultTextProvider实现了Unchainable接口,所以不会执行reflectionProvider.copy方法,也就没有属性的复制。
       当执行ChainAction到该拦截器时,值栈中有三个对象,第一个是ChainAction自己,第二个是TestAction对象,第三个是DefaultTextProvider,
    根据上面的逻辑,就会把TestAction的所有属性复制给ChainAction对象,如果说ChainAction返回的又是chain类型的Result,则又会把ChainAction和TestAction的所有属性复制给下一个Action,依此类推......

       现在我们看一下这句属性复制代码:reflectionProvider.copy(o, invocation.getAction(), ctxMap, excludes, includes);copy方法的第一个参数就是值栈中要被复制属性的对象,第二个参数是当前正在执行的Action,第三个参数是OgnlContext对象,第四、五个参数excludes与includes是一个属性名称集合,excludes表示不需要被复制的属性集合,includes表示要被复制的属性集合,下面是copy方法源码:

    public void copy(Object from, Object to, Map<String, Object> context,
            Collection<String> exclusions, Collection<String> inclusions) {
        ognlUtil.copy(from, to, context, exclusions, inclusions);
    }


    它调用了ognlUtil.copy方法,我们进入该方法:

    public void copy(Object from, Object to, Map<String, Object> context, Collection<String> exclusions, Collection<String> inclusions) {
    	//省略...
    	
        PropertyDescriptor[] fromPds;
        PropertyDescriptor[] toPds;
    
        try {
            fromPds = getPropertyDescriptors(from);//获取源对象(要被复制属性的对象)的属性描述符
            toPds = getPropertyDescriptors(to);//获目标对象(当前正在执行的Action对象)的属性描述符
        } catch (IntrospectionException e) {
            LOG.error("An error occured", e);
    
            return;
        }
    
        Map<String, PropertyDescriptor> toPdHash = new HashMap<String, PropertyDescriptor>();
    
        for (PropertyDescriptor toPd : toPds) {
            toPdHash.put(toPd.getName(), toPd);//将目标对象属性描述符放到一个HashMap中
        }
    	//迭代获取源对象属性描述符
        for (PropertyDescriptor fromPd : fromPds) {
            if (fromPd.getReadMethod() != null) {//当前属性的读方法不为空
                boolean copy = true;
                if (exclusions != null && exclusions.contains(fromPd.getName())) {
                	//如果当前属性包含在exclusions集合中则不复制
                    copy = false;
                } else if (inclusions != null && !inclusions.contains(fromPd.getName())) {
                	//如果配置了inclusions,而当前属性又不包含在inclusions集合中则不复制
                	//如果没有配置inclusions,则只要不包含在exclusions就会复制
                    copy = false;
                }
    
                if (copy == true) {//如果当前属性需要复制
                    PropertyDescriptor toPd = toPdHash.get(fromPd.getName());//获取目标对象相应的属性描述符
                    if ((toPd != null) && (toPd.getWriteMethod() != null)) {//如果属性描述符存在且其写方法不为空则进行复制
                        try {
                            Object expr = compile(fromPd.getName());
                            Object value = Ognl.getValue(expr, contextFrom, from);
                            Ognl.setValue(expr, contextTo, to, value);//真正复制属性的代码
                        } catch (OgnlException e) {
                            // ignore, this is OK
                        }
                    }
    
                }
    
            }
    
        }
    }


       至此,该拦截器要复制哪些属性,要复制哪些对象的属性,以及复制属性的具体规则已经讲解完毕了,最后一句:Ognl.setValue(expr, contextTo, to, value);
    真正复制属性的代码已经涉及到OGNL内部的实现机制,这是很复杂的,但对于理解该拦截器来说不是很重要,这里就不说了,有兴趣的可自行研究。

       现在回到intercept方法,当属性复制完成后调用invocation.invoke();继续执行下一个拦截器...... 

  • 相关阅读:
    ZKEACMS 的两种发布方式
    一步一步教你如何制件 ZKEACMS 的扩展组件/插件
    三张图片看懂ZKEACMS的设计思想
    ZKEACMS 模板组件扩展
    ZKEACMS 如何使用组件自定义样式/编辑样式
    关于简单的Excel多页签底层导出_电子底账导出为例(.net core)
    Javascript中,数学方法可以分成以下几类: constans(常数)、power functions(乘方函数)、trigonometic functions(三角函数)、rounding functions(舍入函数)、random numbers(随机数字)
    AutoCAD.Net/C#.Net QQ群:193522571 对字符串进行四则运算
    AutoCAD.Net/C#.Net QQ群:193522571 利用反射将父类中的属性传递到子类中,实测效率不高
    AutoCAD.Net/C#.Net QQ群:193522571 将DataTable集合 B加入到DataTable A中,应用于两个或以上的只有单行数据的无主键的datatable的合并 Merge
  • 原文地址:https://www.cnblogs.com/james1207/p/3400379.html
Copyright © 2011-2022 走看看