接上文:4.1Java设计模式-----JDK静态代理(Static Proxy)
JDK动态代理是指动态的在内存中构建代理对象(需要我们制定要代理的目标对象实现的接口类型),即利用JDK的API生成指定接口的对象,也称之为JDK代理或者接口代理。
动态代理代码部分,下面将做进一步分析。用到的Person接口和Student被代理类保持不变。
①代理对象的创建(动态代理,需要实现InvocationHandler接口)
package dynamicProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 动态代理实现
*/
public class DynamicProxy implements InvocationHandler {
//被代理类
Student student;
//创建构造方法
public DynamicProxy(Student student){
this.student = student;
}
/**
* 重写invoke方法
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("(此处为前置增强)--我是中介,如下是用户的一些需求");
System.out.println("**************************************************");
//通过反射机制
method.invoke(student,args);
System.out.println("**************************************************");
System.out.println("(此处为后置增强)--我是中介,如上就是我代理用户的需求");
return null;
}
}
②JDK动态代理测试
package dynamicProxy;
import java.lang.reflect.Proxy;
public class test {
public static void main(String[] args) {
//生成$Proxy0的class文件,这一句是生成代理类的class文件(可有可无!!!)
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
Student student = new Student();
/**
* 动态代理参数准备
*/
//1.指明加载被代理类的类加载器--ClassLoader
ClassLoader classLoader = student.getClass().getClassLoader();
//2.指明被代理类实现的接口--Interface
Class>[] interfaces = student.getClass().getInterfaces();
//3.创建被代理类的委托类,之后想要调用被代理类的方法时,都会委托给这个类的invoke(Object proxy, Method method, Object[] args)方法--InvocationHandler
DynamicProxy invocationHandler = new DynamicProxy(student);
/**
* 通过newPrxoyInstance生成动态代理类Object(强转为Person类)
*/
Person proxy= (Person)Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
/**
* 代理用户开始帮助用户找房子
*/
proxy.findHouse();
}
}
代码运行结果:
(此处为前置增强)--我是中介,如下是用户的一些需求
**************************************************
被代理人需求:我是学生A,我现在想找一个10平米的小房间,用来学习
**************************************************
(此处为后置增强)--我是中介,如上就是我代理用户的需求
仔细分析上面的JDK动态代理实现代码,我们看到这里涉及到java反射包下的一个接口InvocationHandler和一个类Proxy。
package java.lang.reflect;
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
这个接口只有一个invoke方法,我们在通过代理类调用被代理类的方法时,最终都会委托给这个invoke方法执行
//通过代理类调用被代理类的方法
proxy.findHouse();
所以我们就可以在这个invoke方法中对被代理类进行增强或做一些其他操作。
Proxy类的public static Object newProxyInstance(ClassLoader loader,Class>[] interfaces,InvocationHandler h)方法内部通过拼接字节码的方式来创建代理类,后面我会反编译出它所创建的代理类看看内容。
我们看这个方法的三个参数:
1.ClassLoader loader:指定一个动态加载代理类的类加载器
2.Class>[] interfaces:指明被代理类实现的接口,之后我们通过拼接字节码生成的类才能知道调用哪些方法
3.InvocationHandler h:这是一个方法委托类,我们通过代理调用被代理类的方法时,就可以将方法名和方法参数都委托给这个委托类。
你看我们现在有了类加载器、类实现的接口、要调用方法的方法名和参数,那么我们就可以做很多事情了。
③反编译Proxy.newProxyInstance所创建的代理类
//这一句是生成代理类的class文件(如报,你需要在工程根目录下创建com/sun/proxy目录)
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
我们在代码中加入上述代码,代码就会保存生成的代理类,名称为$Proxy0.class
④创建的代理类代码$Proxy0.class文件,反编译后【其中注释是自己加上去的】
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.sun.proxy;
import dynamicProxy.Person;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
/**
*代理类也实现了Person接口,看起来和静态代理的方式也是一样的
*同时代理类也继承了Proxy类
*/
public final class $Proxy0 extends Proxy implements Person {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
//实现了Person接口的方法,这就是我们调用这个方法Proxy.newProxyInstance必须提供第二个参数的作用
public final void findHouse() throws {
try {
// 我们看到通过调用代理类的方法时,最终方法都会委托给InvocationHandler实现类的invoke方法
// m3为代理类通过反射获得的Method
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
//代理类通过反射 获得的接口方法Method
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("dynamicProxy.Person").getMethod("findHouse");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
JDK动态代理的优点和缺点
优点:解决了静态代理的缺点。动态代理让我们在不直接访问某些对象的情况下,通过代理机制也可以访问被代理对象的方法,这种技术可以应用在很多地方比如RPC框架,Spring AOP机制。
缺点:动态代理机制必须要求被代理类实现某个方法,这样在生成代理类的时候才能知道重写哪些方法。这样如果一个没有实现任何接口的类,就无法通过动态代理机制来进行代理操作了。
附链接:4. Java设计模式-----代理模式(Proxy Pattern)
4.1 Java设计模式-----JDK静态代理(Static Proxy)
4.3 Java设计模式-----Cglib动态代理(Cglib Proxy)