前言
你想成为java master吗?
参考链接:https://ch1e.cn/post/spring-xi-lie-fan-xu-lie-hua-lian/
花费近5小时,从头到尾80%独立分析,只从参考文章瞄了一眼MethodInvokeTypeProvider
,ObjectFactoryDelegatingInvocationHandler
,AnnotationInvocationHandler
这三个类,其余部分全靠自己打通,最终弹出calc。
动态代理
之前对动态代理只有初步认识,独立分析完spring1一切都清晰了。
关于动态代理的原理与demo网上有。
动态代理最重要的两个部分:实现的接口与handler。
使用Proxy.newProxyInstance
创建代理时,接口列表写了什么,代理对象就实现了什么接口。
而handler对象用来hook该对象的所有方法,hook的具体操作在handler的invoke方法里实现。有一些方法不会被hook:
所以总结一下动态代理大概原理:动态创建一个类(com.sun.proxy.$Proxy0 1 2…),这个类实现了任意接口,然后new 该类产生的对象即代理对象。代理对象有invocationHandler对象,调用代理对象的方法都会经过invocationHandler对象的invoke方法。
所以动态代理发挥作用最关键的就是InvocationHandler。
AnnotationInvocationHandler
这个类的invoke方法:
该handler的作用:当调用动态代理的方法时,将方法名作为key,从memberValues取出value作为该方法的返回值。注意返回值类型要相同。这个handler参与反序列化时,memberValues是任意可控的,key和value也任意可控。
所以总结这个handler的作用:令某个方法返回任意对象。这个对象也可以是动态代理对象,只需要实现返回值的接口即可。
ObjectFactoryDelegatingInvocationHandler
org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler
this.objectFactory.getObject()
没细看底层实现, 不知是否可控。
但是若使用上面说的AnnotationInvocationHandler,能达到的效果是令某个方法返回任意对象,所以this.objectFactory.getObject()
也是任意可控的。
所以总结ObjectFactoryDelegatingInvocationHandler的作用:将调用某个对象的method,变为调用任意可控对象的method。注意类型和参数要匹配。
MethodInvokeTypeProvider
org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider
spring1的入口就是这里。
这个类的readObject:
开始分析
环境
<dependency> |
jdk版本8u66,8u73或者更高打不通。
入口在MethodInvokeTypeProvider#readObject
。
findMethod
先看findMethod。
首先要知道sink点,是TemplatesImpl#getOutputProperties
或者TemplatesImpl#newTransformer
。
那么容易想到尝试的是:
ReflectionUtils.findMethod(Templates.class, "getOutputProperties"); |
但是getType要求返回值类型是java.lang.reflect.Type
。所以现在的问题是如何让this.provider.getType()
既是Templates又是Type?
解决方法是令this.provider.getType()
返回一个动态代理,我们手动让这个代理实现Templates和Type两个接口。
Class<?> typeInterface = Class.forName("java.lang.reflect.Type"); |
那么如何令this.provider.getType()
返回一个动态代理?前面说过,AnnotationInvocationHandler的作用是令某个方法返回任意对象。
那么只需要令this.provider
为一个代理,这个代理的handler是AnnotationInvocationHandler
,并设置好memberValues,此时this.provider.getType
返回的就是任意对象。
此时,findMethod已经能获取到method为Templates#getOutputProperties
。
现在要令调用该method的对象是TemplatesImpl,怎么办?
此时代理的handler还没确定。
invokeMethod
ReflectionUtils.invokeMethod(method, this.provider.getType());
这条语句,会调用动态代理的Templates#getOutputProperties方法。
前面说过,ObjectFactoryDelegatingInvocationHandler
这个handler的核心作用是将调用某个对象的method,变为调用任意可控对象的method。
那么我们可以将调用动态代理的Templates#getOutputProperties方法这一操作,变为调用TemplatesImpl的Templates#getOutputProperties这一操作。
代码实现
第一个代理,用于调用getType时返回第二个代理。
public static Object proxy1() throws Exception{ |
第二个代理,设置给objectFactory。
public static Object proxy2() throws Exception{ |
第三个代理,用于调用objectFactory的getObject时返回TemplatesImpl
public static Object proxy3() throws Exception{ |
反序列化入口
public static void main(String[] args) throws Exception{ |
修改final属性
public static void setFinalFieldValue(Object obj,String name,Object val) throws Exception{ |
JDK版本
在8u66中,创建出AnnotationInvocationHandler对象并修改memberValues,经过序列化和反序列化,memberValues不变。
在8u73中,创建出AnnotationInvocationHandler对象并修改memberValues,经过序列化和反序列化,memberValues改变。
不同之处就在AnnotationInvocationHandler#readObject。
在高版本JDK中,readObject时会检查type属性,type属性是一个注解。注解有一个属性叫做memberTypes,是个map。
假如memberValues序列化前有如下键值对:A1->B1,A2->B2
memberTypes的键值对:A1->C1
那么经过readObject,最终的memberValues的键值对:A1->B1或C1,A2->null。
memberTypes是什么?
如Target这个注解,他的memberTypes的key是value,值不知道是什么。
所以在高版本JDK中,memberValues不是任意可控了,必须找到某个注解,这个注解的memberTypes的key里有getType和getObject。