jdk的动态代理

时间:2022-12-05 10:25:14

至于jdk的动态代理怎么用此处并不多说,现在是更深一步的理解,jdk的Proxy类到底做了什么。

Proxy.newProxyInstance可以生成代理类,此方法有三个参数(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)分别是类加载器,接口数组,InvocationHandler的实现类。

简而言之,Proxy通过newProxyInstance方法生成了一个实现了第二个参数所有接口的实现类,并且内部组合了一个InvocationHandler h,当调用代理类实现的接口(第二个参数)中的方法时,该方法调用InvocationHandler 接口中的invoke方法,把自身作为第一个参数,该方法的Method对象作为第二个参数,该方法的所有参数以Object数组的形式作为第三个参数传递过去,用户可以回调invoke方法,因此自己实现类里的invoke方法的到了调用。

下面是一个自己写的MyProxy.

package com.shalf.myproxy;

public interface Animal {
public void breathe();
}

package com.shalf.myproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Client {
public static void main(String[] args) {
Animal dog = new Dog();
// 使用JDK的java.lang.reflect.Proxy生成代理实例
Animal dogProxy = (Animal) Proxy.newProxyInstance(dog.getClass().getClassLoader(), dog.getClass().getInterfaces(), new ProxyInvoke(dog));
dogProxy.breathe();

System.out.println("---------------优美的分割线在此卖萌---------------");

// 使用自己的代理类生成代理实例
// 需要注意的是这里的MyProxy类和JDK中java.lang.reflect.Proxy并不相同。只是他们的原理大致相同,实现的细节并不同。JDK中生成$Proxy1并不是拼字符串,而是直接生成二进制码。
Animal myDogProxy = (Animal) MyProxy.newProxyInstance(dog.getClass().getClassLoader(), Animal.class, new ProxyInvoke(dog));
myDogProxy.breathe();

// System.out.println(System.getProperty("user.dir"));
// System.out.println(Class.class.getClass().getResource("/").getPath());
}
}

/**
* 此类并不是代理类,只是根据这个类生成代理类,生成的代理类引用InvocationHandler实例的invoke方法,属于回调函数。
*
* @author shalf
*
*/
class ProxyInvoke implements InvocationHandler {

private final Object obj;

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("DogProxy.invoke()");
return method.invoke(obj, args);
}

public ProxyInvoke(Object obj) {
super();
this.obj = obj;
}
}

package com.shalf.myproxy;

public class Dog implements Animal {

@Override
public void breathe() {
System.out.println("Dog.breathe()");
}

}

package com.shalf.myproxy;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

public class MyProxy {

private static String filePath = System.getProperty("user.dir") + "/src/";
// private static String packageName = MyProxy.class.getPackage().getName();
private static String name = "$Proxy1";
private static String fileName = filePath+ name + ".java";

public static Object newProxyInstance(ClassLoader loader, Class<?> infce, InvocationHandler h) {
Object obj = null;
try {
// 创建源文件
String src = createClass(infce);
// 写到文件
toFile(src);
// 编译文件
compileFile();
// 获得实例
obj = getInstance(h);
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}

/**
* 创建类的代码 方法最核心的代码,h是构造$Proxy1时传入的handler,本例中就是DogProxy对象
* 从而达到我们最初要在move方法前加日志逻辑的的目的
* 下面要做的就是把我们拼好的字符串生成类并load到内存就可以了这样就实现了动态生成代理类并加自己想加的逻辑
*
* @param infce
* @return
*/
private static String createClass(Class<?> infce) {
String methodStr = "";
String rt = "\r\n";
// 利用反射,获得infce接口中方法,本例中就是获得Animal接口的方法breathe
Method[] methods = infce.getMethods();

// 拼接infce中所有方法字符串,用来重写infce中的方法,本例中拼出来就是重写的breathe方法
for (Method m : methods) {
methodStr += " @Override" + rt + " public void " + m.getName() + "() {" + rt + " try {" + rt + " Method md = " + infce.getName() + ".class.getMethod(\"" + m.getName() + "\");"
+ rt + " h.invoke(this, md, md.getParameterTypes());" + rt + " }catch(Throwable e) {e.printStackTrace();}" + rt + " }" + rt;
}

String src ="import java.lang.reflect.Method;" + rt + "import java.lang.reflect.InvocationHandler;" + rt + "public class $Proxy1 implements "
+ infce.getName() + "{" + rt + " public $Proxy1(InvocationHandler h) {" + rt + " this.h = h;" + rt + " }" + rt + " InvocationHandler h;" + rt + methodStr + rt + "}";

return src;
}

/**
* load into memory and create an instance加载进内存并创建对象
*
* @param h
* @return
* @throws MalformedURLException
* @throws ClassNotFoundException
* @throws NoSuchMethodException
* @throws InstantiationException
* @throws IllegalAccessException
* @throws InvocationTargetException
*/

private static Object getInstance(InvocationHandler h) throws Exception {
String url = "file:/" + filePath+ "/";
URL[] urls = new URL[] { new URL(url) };
URLClassLoader ul = new URLClassLoader(urls);
Class<?> c = ul.loadClass(name);
//拿到那个(本例也只有这一个)带InvocationHandler参数的构造器
Constructor<?> ctr = c.getConstructor(InvocationHandler.class);
return ctr.newInstance(h);
}

/**
* 编译文件
*
* @throws IOException
*/
private static void compileFile() throws IOException {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
compiler.getTask(null, fileMgr, null, null, null, fileMgr.getJavaFileObjects(fileName)).call();
fileMgr.close();
}

/**
* 写到文件
*
* @param src
* @throws IOException
*/
private static void toFile(String src) throws IOException {
File f = new File(fileName);
FileWriter fw = new FileWriter(f);
fw.write(src);
fw.flush();
fw.close();
}

下面是生成的代理类的源代码,当然了jdk不是生成源代码,而是在内存直接生成字节码。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class $Proxy1 implements com.shalf.myproxy.Animal {
public $Proxy1(InvocationHandler h) {
this.h = h;
}

InvocationHandler h;

@Override
public void breathe() {
try {
Method md = com.shalf.myproxy.Animal.class.getMethod("breathe");
h.invoke(this, md, md.getParameterTypes());
} catch (Throwable e) {
e.printStackTrace();
}
}

}

}