JDK动态代理
JDK提供了Java.lang.reflect.Proxy类来实现动态代理的,可通过它的newProxyInstance来获得代理实现类。同时对于代理的接口的实际处理,是一个java.lang.reflect.InvocationHandler,它提供了一个invoke方法供实现者提供相应的代理逻辑的实现。
例子
以下代码只是为了了解JDK动态代理的底层实现原理,其实一些类是jdk包中早就已经实现了的。比如MyProxy
Test.class
public class Test {
public static void main(String[] args) {
Object realProxy=MyProxy.newProxyInstance(new MyClassLoader(),new Class<?>[]{TargetClass.class},new MyDynamiProxy(new TargetImpl()));
TargetClass targetClass=(TargetClass)realProxy;
targetClass.sayHello();
}
}
TargetClass.class
public interface TargetClass {
public void sayHi();
public void sayHello();
}
TargetImpl.class
public class TargetImpl implements TargetClass{
public void sayHi() {
System.out.println("HI");
}
public void sayHello() {
System.out.println("Hello");
}
}
MyDynamiProxy.class
public class MyDynamiProxy implements MyInvocationHandle {
//这并不是真正的代理类,是协助代理类的一部分
public Object target;
public MyDynamiProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法输出前");
Object result=method.invoke(target,args);
System.out.println("方法输出后");
return null;
}
}
MyProxy.class
public class MyProxy {
public static final String Ln="\r\n";
public static Object newProxyInstance(MyClassLoader loader,
Class<?>[] interfaces,
MyInvocationHandle h){
// 1.动态生成一个.java的源文件
String code=generateCode(interfaces);
// 2.把生成的这个.java源文件保存在磁盘上
try {
String filePath=MyProxy.class.getResource("").getPath();
// filePath=java.net.URLDecoder.decode(filePath,"utf-8");
File file=new File("D:\\文件\\学习制作网页\\dailimoshi.com.proxy\\src\\main\\java\\comMyProxy\\"+"$Proxy0.java");
FileWriter fw=new FileWriter(file);
fw.write(code);
fw.flush();
fw.close();
// 3.把这个.java源文件编译成.class文件
// 创建一个java编译器对象
JavaCompiler compiler= ToolProvider.getSystemJavaCompiler();
// java源代码文件管理器
StandardJavaFileManager manager =compiler.getStandardFileManager(null,null,null);
// java源文件的迭代器
Iterable iterable=manager.getJavaFileObjects(file);
// 获取一个编译任务
JavaCompiler.CompilationTask task =compiler.getTask(null,manager,null,null,null,iterable);
// 执行编译
task.call();
// 关闭文件管理器
manager.close();
// 4.把编译后的.class文件加载到jvm内存中
Class clazz=loader.findClass("$Proxy0");
// 5.根据加载到jvm中的.class字节码文件生成class文件,然后创建class类的对象
Constructor constructor =clazz.getConstructor(MyInvocationHandle.class);
return constructor.newInstance(h);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static String generateCode(Class<?>[] interfaces){
StringBuffer sb=new StringBuffer();
sb.append("package comMyProxy;"+Ln);
sb.append("import java.lang.reflect.Method;"+Ln);
sb.append("public class $Proxy0 implements "+interfaces[0].getName()+"{"+Ln);
sb.append("public MyInvocationHandle h;"+Ln);
sb.append("public $Proxy0(MyInvocationHandle h){"+Ln);
sb.append("this.h=h;"+Ln);
sb.append("}"+Ln);
for(Method m:interfaces[0].getMethods()){
sb.append("public "+m.getReturnType()+" "+m.getName()+"(){"+Ln);
sb.append("try{"+Ln);
sb.append("Method m="+interfaces[0].getName()+".class.getMethod(\""+m.getName()+"\",new Class[]{});"+Ln);
sb.append("this.h.invoke(this,m,null);"+Ln);
sb.append("}catch(Throwable e){"+Ln);
sb.append("e.printStackTrace();"+Ln);
sb.append("}"+Ln);
sb.append("}"+Ln);
}
sb.append("}"+Ln);
return sb.toString();
}
}
MyClassLoader.class
package comMyProxy;
import java.io.*;
//自定义一个类加载器
//bootstrap ClassLoader jdk/jre目录下的jre包加载
//ext ClassLoader jdk/ext/目录下的jar包加载
//App ClassLoader 应用加载
public class MyClassLoader extends ClassLoader{
public File classPathFile;
public MyClassLoader() {
// String classPath=MyClassLoader.class.getResource("").getPath();
String classPath="D:\\文件\\学习制作网页\\dailimoshi.com.proxy\\src\\main\\java\\comMyProxy\\";
this.classPathFile = new File(classPath);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String className=MyClassLoader.class.getPackage().getName()+"."+name;
if(classPathFile!=null){
File classFile=new File(classPathFile,name.replaceAll("\\.","/")+".class");
if (classFile.exists()){
FileInputStream fis=null;
ByteArrayOutputStream bos=null;
try {
fis=new FileInputStream(classFile);
byte[] bytes=new byte[4096];
bos=new ByteArrayOutputStream();
int len;
while ((len=fis.read(bytes))!=-1){
bos.write(bytes,0,len);
}
return defineClass(className,bos.toByteArray(),0,bos.size());
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if(fis!=null)fis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(bos!=null)bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
return null;
}
}
$Proxy0.java
底层生成的代理类的源文件
动态生成的代理类有如下特性:
-
继承了Proxy类,实现了代理的接口,由于java不能多继承,这里已经继承了Proxy类了,不能再继承其他的类,所以JDK的动态代理不支持对实现类的代理,只支持接口的代理。
-
提供了一个使用InvocationHandler作为参数的构造方法。
-
生成静态代码块来初始化接口中方法的Method对象,以及Object类的equals、hashCode、toString方法。
-
重写了Object类的equals、hashCode、toString,它们都只是简单的调用了InvocationHandler的invoke方法,即可以对其进行特殊的操作,也就是说JDK的动态代理还可以代理上述三个方法。
-
代理类实现代理接口的sayHello方法中,只是简单的调用了InvocationHandler的invoke方法,我们可以在invoke方法中进行一些特殊操作,甚至不调用实现的方法,直接返回。
package comMyProxy;
import java.lang.reflect.Method;
public class $Proxy0 implements comMyProxy.TargetClass{
public MyInvocationHandle h;
public $Proxy0(MyInvocationHandle h){
this.h=h;
}
public void sayHello(){
try{
Method m=comMyProxy.TargetClass.class.getMethod("sayHello",new Class[]{});
this.h.invoke(this,m,null);
}catch(Throwable e){
e.printStackTrace();
}
}
public void sayHi(){
try{
Method m=comMyProxy.TargetClass.class.getMethod("sayHi",new Class[]{});
this.h.invoke(this,m,null);
}catch(Throwable e){
e.printStackTrace();
}
}
}
$Proxy0.class
底层生成的代理类的class文件
public class $Proxy0 implements TargetClass {
public MyInvocationHandle h;
public $Proxy0(MyInvocationHandle var1) {
this.h = var1;
}
public void sayHello() {
try {
Method var1 = TargetClass.class.getMethod("sayHello");
this.h.invoke(this, var1, (Object[])null);
} catch (Throwable var2) {
var2.printStackTrace();
}
}
public void sayHi() {
try {
Method var1 = TargetClass.class.getMethod("sayHi");
this.h.invoke(this, var1, (Object[])null);
} catch (Throwable var2) {
var2.printStackTrace();
}
}
}