日期:2014-05-20  浏览次数:20702 次

Method类和cglib有鲜为人知的一腿?
请看我另一篇帖子http://topic.csdn.net/u/20100531/00/0fb8eeb7-e193-4e6f-9399-ef2aa0742d6b.html?300891006
经过我长达半天的追踪代码,发现了问题的所在,顺便给出这个帖子的解决方法:
问题就出在struts2中的chain拦截器中。当页面中使用了struts2标签中的<s:action>标签,或者其他显示返回chain结果时,在chain拦截器中,边会对一个上一个action的属性进行copy,而copy的实现是调用com.opensymphony.xwork2.ognl.OgnlUtil 类中的copy方法完成的,最终问题就是在这个方法中,请看代码:
Java code

    public void copy(Object from, Object to, Map<String, Object> context, Collection<String> exclusions, Collection<String> inclusions) {
        if (from == null || to == null) {
            LOG.warn("Attempting to copy from or to a null source. This is illegal and is bein skipped. This may be due to an error in an OGNL expression, action chaining, or some other event.");

            return;
        }

        TypeConverter conv = getTypeConverterFromContext(context);
        Map contextFrom = Ognl.createDefaultContext(from);
        Ognl.setTypeConverter(contextFrom, conv);
        Map contextTo = Ognl.createDefaultContext(to);
        Ognl.setTypeConverter(contextTo, conv);

        PropertyDescriptor[] fromPds;
        PropertyDescriptor[] toPds;

        try {
            fromPds = getPropertyDescriptors(from);
            toPds = getPropertyDescriptors(to);
        } 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);
        }

        for (PropertyDescriptor fromPd : fromPds) {
            //修改部分 begin
            if("targetSource".equals(fromPd.getName()) || "callback".equals(fromPd.getName())
                    || "callbacks".equals(fromPd.getName())){
                continue;
            }
         //修改部分 end
            if (fromPd.getReadMethod() != null) {
                boolean copy = true;
                if (exclusions != null && exclusions.contains(fromPd.getName())) {
                    copy = false;
                } else if (inclusions != null && !inclusions.contains(fromPd.getName())) {
                    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
                        }
                    }

                }

            }

        }
    }



将此类的方法修改如上,便可以解决我上个帖子出现的问题了。

现在说一下一个新的问题,望有牛人解答。
罗嗦说一下错误出现的稍微深入点的原因。当处理s:action标签时,在调用此copy()方法时,action是通过cglib动态生成的类,除了action原本的属性,cglib类还给这个action加上了几个属性,至于何用,我却也不知。这几个属性有targetSource,callback,callbacks等等。上面修改了的copy方法并不对这些属性进行复制,从而后面的action的这些属性不会给前面的action这些属性覆盖。而我继续追踪下去,直道调用Method.invoke()之前都不曾修改这些属性。
这意味着什么?
我个人猜测就是这些cglib附加上的属性,是Method.invoke中会用到的。可惜Method方法暂且能用反编译器反编译出来,然而里面调用到一个sun.reflect.MethodAccessor类时,却是功力不足,找不下去了。
据我了解,java包的Method等反射机制相关的sun都没有放出源码,而这些实现都是虚拟机来负责的(不知有无了解错),所以请对虚拟机和cglib两者熟悉的高人前来指点指点。

------解决方案--------------------
呵呵 很多框架 你不了解他的底层实现代码,很难用好,至于虚拟机和cglib的底层我还没研究,所以解答不了你,所以楼主加油。
------解决方案--------------------
cglib多用来搞定aop的功能。

比如批量拦截记录日志,声明式事务统一管理,权限拦截。

你可以去网上搜索reflect,proxy。要是能学学aop就更容易懂了。

BTW。用cglib搞了action,说明你把action定义到spring中,还加上了一层代理,个人觉得,还是不要让spring这样处理action比较好。因为每次action都需要新生成intance,你每次都加代理比较浪费,而且我们又不需要在action层处理事务,所以直接返还简单的pojo会比较好。