概念
代理模式Java当中最常用的设计模式之一 , 提供了对目标对象额外的访问方式 , 即通过代理对象访问目标对象.
举个例子 , 存在一个对象A , 但是开发人员不希望程序直接访问对象A , 而是通过访问一个中介对象B来间接访问对象A , 以达成访问对象A的目的。此时 , 对象A被称为 “委托类” , 对象B被称为 “代理类”
代理模式特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。
Java的代理机制分为静态代理和动态代理。
静态代理示例讲解
JDK动态代理
JDK原生动态代理利用拦截器和反射来实现,JDK原生动态代理的核心API为如下两个类 。
java.lang.reflect.Proxy
java.lang.reflect.InvocationHandler
java.lang.reflect.Proxy
:负责动态构建代理类
该类的功能为 : 提供四个静态方法来为一组接口动态地生成的代理类并返回代理类的实例对象
getProxyClass(ClassLoader,Class<?>[] interfaces):获取指定类加载器和一组接口的动态代理类的类对象。
newProxyInstance(ClassLoader,Class<?>[],InvocationHandler):指定类加载器,一组接口,调用处理器;
isProxyClass(Class<?>):判断获取的类是否为一个动态代理类;
getInvocationHandler(Object):获取指定代理类实例查找与它相关联的调用处理器实例;
java.lang.reflect.InvocationHandler
:负责提供调用代理操作
该接口定义了一个 invoke()
方法 , 用于集中处理在动态代理类对象上的方法调用 . 当程序通过代理对象调用某一个方法时 , 该方法调用会被自动转发到 InvocationHandler.invoke()
方法来进行调用。
动态代理例子(看看怎么用)
接口类
package com.study.dy_proxy;
public interface test_inter {
public void say();
}
委托类
package com.study.dy_proxy;
public class test_entrust implements test_inter {
@Override
public void say() {
System.out.println("hello!");
}
}
实现调用处理器的也称中介类
package com.study.dy_proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class test_handler implements InvocationHandler {
private Object target;//target为委托类对象
public test_handler(Object target){
this.target = target;
}
//实现invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("test proxy");
Object result = method.invoke(target,args);
return result;
}
}
测试类
package com.study.dy_proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class test {
public static void main(String[] args) {
test_entrust entrust = new test_entrust();
//classloader
ClassLoader classLoader = entrust.getClass().getClassLoader();
//interfaces
Class[] interfaces = entrust.getClass().getInterfaces();
//调用处理器
InvocationHandler invocationHandler = new test_handler(entrust);
//代理对象
test_inter proxy = (test_inter) Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);
proxy.say();
}
}
从而成功实现
YSoSerial
ysoserial是集合了各种java反序列化payload的工具,关于该工具的分析
解密ysoserial
java.lang.reflect.Proxy
java.lang.reflect.Proxy
类有个native
的defineClass0
方法可实现动态创建类对象。
java.lang.reflect.Proxy
类是通过创建一个新的Java类(类名为com.sun.proxy.$ProxyXXX)
的方式来实现无侵入的类方法代理功能的。
需要注意以下技术细节和特点:
- 动态代理的必须是接口类,通过
动态生成一个接口实现类
来代理接口的方法调用(反射机制
)。 - 该类继承于
java.lang.reflect.Proxy
并实现了需要被代理的接口类,因为java.lang.reflect.Proxy
类实现了java.io.Serializable
接口,所以被代理的类支持序列化/反序列化
。 - 如果动过动态代理生成了多个动态代理类,新生成的类名中的
0
会自增,如com.sun.proxy.$Proxy0/$Proxy1/$Proxy2
。 - 该类方法中包含了被代理的接口类的所有方法,通过调用动态代理处理类(
InvocationHandler
)的invoke
方法获取方法执行结果。
动态代理类实例的序列化
动态代理生成的类在反序列化/反序列化
时不会序列化该类的成员变量,并且serialVersionUID
为0L
也将是说将该类的Class
对象传递给java.io.ObjectStreamClass
的静态lookup
方法时,返回的ObjectStreamClass
实例将具有以下特性:
- 调用其
getSerialVersionUID
方法将返回0L
。 - 调用其
getFields
方法将返回长度为零的数组。 - 调用其
getField
方法将返回null
。
但其父类(java.lang.reflect.Proxy
)在序列化时不受影响,父类中的h
变量(InvocationHandler
)将会被序列化,这个h
存储了动态代理类的处理类实例以及动态代理的接口类的实现类的实例。
(学习java序列化和反序列化的时候再补补)