动态代理
JDK动态代理
使用案例
JDK动态代理 只能 代理接口;CGLIB动态代理可以代理类;
静态代理缺点
- 当接口增删改方法,那么代理类已得要跟着修改;
- 代理类的每个接口对象对应一个委托对象,如果委托对象很多,代理类就会变得异常臃肿
https://www.jianshu.com/p/85d181d7d09a
定义委托对象接口
public interface Subject {
void test();
}
委托对象接口实现类
public class RealSubject implements Subject {
@Override
public void test() {
System.out.println("this is dynamic RealSubject test");
}
}
动态代理类方法调用处理程序
public class DynamicProxy implements InvocationHandler {
Object mObj;
public DynamicProxy(Object pObj){
mObj = pObj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
if(mObj != null){
System.out.println("动态代理前");
result = method.invoke(mObj, args);
System.out.println("动态代理后");
}
return result;
}
}
使用
public class Client {
public static void main(String[] args){
RealSubject realSubject = new RealSubject();
DynamicProxy proxy = new DynamicProxy(realSubject);
Subject subject = (Subject) Proxy.newProxyInstance(
realSubject.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(),
proxy
);
subject.test();
}
}
JDK动态代理原理
https://www.cnblogs.com/liuyun1995/p/8144706.html
JDK动态代理使用了Proxy.newProxyInstance方法动态创建代理类
Proxy.newProxyInstance关键代码
// 参数1:指定类加载器;参数2:接口列表;参数3:接口的实现类(委托对象)
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
。。。
//动态代理类方法调用处理程序即这里的h不能为空
Objects.requireNonNull(h);
//委托对象的接口列表
final Class<?>[] intfs = interfaces.clone();
//重点,生成代理类的Class类
Class<?> cl = getProxyClass0(loader, intfs);
//获取 生成的代理类的 有参 构造函数
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
// 实例化 生成的代理类,并返回
return cons.newInstance(new Object[]{h});
。。。
}
可以看到,newProxyInstance方法调用getProxyClass0方法生成了代理类的Class类,最终通过反射实例化了这个代理类;
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
//接口数量不能超过65535
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
//根据类加载器和接口数组,从 WeakCache缓存中 获取代理类Class对象
return proxyClassCache.get(loader, interfaces);
}
泛型类WeakCache<K,P,V>是Proxy类中定义的一个静态成员变量,其中 K是类加载器,P是目标类的接口类数组,V就是产生的代理类Class对象。即根据P、V生成代理类Class并存放在WeakCache中。
最终通过Proxy的内部类ProxyClassFactory的private byte[] generateClassFile()
方法生成代理类,generateClassFile方法本质上是通过拼装.class
文件来生成代理类的。
private byte[] generateClassFile() {
//第一步, 将所有的方法组装成ProxyMethod对象
//首先为代理类生成toString, hashCode, equals等代理方法
addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class);
//遍历每一个接口的每一个方法, 并且为其生成ProxyMethod对象
for (int i = 0; i < interfaces.length; i++) {
Method[] methods = interfaces[i].getMethods();
for (int j = 0; j < methods.length; j++) {
addProxyMethod(methods[j], interfaces[i]);
}
}
//对于具有相同签名的代理方法, 检验方法的返回值是否兼容
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
checkReturnTypes(sigmethods);
}
//第二步, 组装要生成的class文件的所有的字段信息和方法信息
try {
//添加构造器方法
methods.add(generateConstructor());
//遍历缓存中的代理方法
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
for (ProxyMethod pm : sigmethods) {
//添加代理类的静态字段, 例如:private static Method m1;
fields.add(new FieldInfo(pm.methodFieldName,
"Ljava/lang/reflect/Method;", ACC_PRIVATE | ACC_STATIC));
//添加代理类的代理方法
methods.add(pm.generateMethod());
}
}
//添加代理类的静态字段初始化方法
methods.add(generateStaticInitializer());
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception");
}
//验证方法和字段集合不能大于65535
if (methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
}
if (fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
}
//第三步, 写入最终的class文件
//验证常量池中存在代理类的全限定名
cp.getClass(dotToSlash(className));
//验证常量池中存在代理类父类的全限定名, 父类名为:"java/lang/reflect/Proxy"
cp.getClass(superclassName);
//验证常量池存在代理类接口的全限定名
for (int i = 0; i < interfaces.length; i++) {
cp.getClass(dotToSlash(interfaces[i].getName()));
}
//接下来要开始写入文件了,设置常量池只读
cp.setReadOnly();
ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);
try {
//1.写入魔数
dout.writeInt(0xCAFEBABE);
//2.写入次版本号
dout.writeShort(CLASSFILE_MINOR_VERSION);
//3.写入主版本号
dout.writeShort(CLASSFILE_MAJOR_VERSION);
//4.写入常量池
cp.write(dout);
//5.写入访问修饰符
dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);
//6.写入类索引
dout.writeShort(cp.getClass(dotToSlash(className)));
//7.写入父类索引, 生成的代理类都继承自Proxy
dout.writeShort(cp.getClass(superclassName));
//8.写入接口计数值
dout.writeShort(interfaces.length);
//9.写入接口集合
for (int i = 0; i < interfaces.length; i++) {
dout.writeShort(cp.getClass(dotToSlash(interfaces[i].getName())));
}
//10.写入字段计数值
dout.writeShort(fields.size());
//11.写入字段集合
for (FieldInfo f : fields) {
f.write(dout);
}
//12.写入方法计数值
dout.writeShort(methods.size());
//13.写入方法集合
for (MethodInfo m : methods) {
m.write(dout);
}
//14.写入属性计数值, 代理类class文件没有属性所以为0
dout.writeShort(0);
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception");
}
//转换成二进制数组输出
return bout.toByteArray();
}
生成Class文件主要分为三步:
第一步:收集所有要生成的代理方法,将其包装成ProxyMethod对象并注册到Map集合中。
第二步:收集所有要为Class文件生成的字段信息和方法信息。
第三步:完成了上面的工作后,开始组装Class文件。
最终生成的代理类为
public class Proxy0 extends Proxy implements Subject {
//第一步, 生成构造器
protected Proxy0(InvocationHandler h) {
super(h);
}
//第二步, 生成静态域
private static Method m0; //hashCode方法
private static Method m1; //equals方法
private static Method m2; //toString方法
private static Method m4; //我们自定义的test方法
//第三步, 生成代理方法
@Override
public int hashCode() {
try {
return (int) h.invoke(this, m0, null);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public boolean equals(Object obj) {
try {
Object[] args = new Object[] {obj};
return (boolean) h.invoke(this, m1, args);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public String toString() {
try {
return (String) h.invoke(this, m2, null);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public void test(User user) {
try {
//构造参数数组, 如果有多个参数往后面添加就行了
Object[] args = new Object[] {user};
h.invoke(this, m3, args);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
//第四步, 生成静态初始化方法
static {
try {
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
return;
m3 = Class.forName("com.zy.proxy.Subject").getMethod("test", new Class[0]);
}
catch (NoSuchMethodException localNoSuchMethodException) {
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException) {
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
可以看到生成的代理类,继承了Proxy并实现了我们自定义的接口;
public class Proxy implements java.io.Serializable {
...
protected InvocationHandler h;
private Proxy() {
}
...
}
需要注意的地方
- 生成的代理类Proxy0继承了父类Proxy的
protected InvocationHandler
属性(Proxy0类中的h); - 调用
h.invoke
()方法,执行 被代理类(我们自定义的 实现了InvocationHandler的DynamicProxy类)的方法; - 需要实现 我们自定义的接口Subject来实现 test方法;
- 可以看到Proxy0和静态代理有几分相似;
JDK动态代理为什么是基于接口?
- 生成的代理类继承了Proxy,由于java是单继承,所以只能实现接口