JDK动态代理
public interface Person {
void doSomething();
}
public class PersonImpl implements Person {
@Override
public void doSomething() {
try {
Thread.sleep(2000); // 模拟耗时
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("doSomething");
}
}
public class JDKProxyPerson implements InvocationHandler {
// 被代理的对象
private Object target;
public Object getInstance(Object target) {
this.target = target;
Class<?> clazz = target.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis();
Object result = method.invoke(target, args);
long end = System.currentTimeMillis();
System.out.println("总耗时:" + (end - start));
return result;
}
}
public class Main {
public static void main(String[] args) {
Person person = (Person) new JDKProxyPerson().getInstance(new PersonImpl());
person.doSomething();
}
}
JDK动态代理生成对象的步骤如下:
- 获取被代理对象的引用,并且获取它的所有接口,反射获取
- JDK动态代理类重新生成一个新的类,同时新的类要实现被代理类实现的所有接口
- 动态生成Java代码
- 编译新生成的java代码.class文件
- 重新加载到JVM
以上过程就叫字节码重组。JDK中有一组规范,在ClassPath下只要是$开头的.class文件,一般都是自动生成的。
下面将内存中的对象字节码通过文件流输出到一个新的.class文件,然后利用反编译工具查看其源代码。
public static void main(String[] args) {
byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});
try {
FileOutputStream fos = new FileOutputStream("E:\\$Proxy0.class");
fos.write(bytes);
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
运行以上代码,我们能在E盘下找到一个$Proxy0.class文件,使用IDEA直接可以打开查看源代码
import com.lzc.proxy.Person;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
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);
}
}
public final void doSomething() throws {
try {
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 {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.lzc.proxy.Person").getMethod("doSomething");
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());
}
}
}
可以发现,$Proxy0继承了Proxy类,同时实现了Person接口,而且重写了doSomething方法。在静态块中利用反射找到了目标对象的所有方法,而且保存了所有方法的引用,重写的方法用反射调用对象的方法。上面这些代码都是JDK帮我们自动生成的。
CGLib动态代理
public class CGLibPerson {
public void doSomething() {
try {
Thread.sleep(2000); // 模拟耗时
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("doSomething");
}
}
public class CGLibProxyPerson implements MethodInterceptor {
public Object getInstance(Class<?> clazz) {
Enhancer enhancer = new Enhancer();
// 将clazz设置为即将生成的新类的父类
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
long start = System.currentTimeMillis();
Object result = methodProxy.invokeSuper(o, objects);
long end = System.currentTimeMillis();
System.out.println("总耗时:" + (end - start));
return result;
}
}
public static void main(String[] args) {
CGLibPerson person = (CGLibPerson) new CGLibProxyPerson().getInstance(CGLibPerson.class);
person.doSomething();
}
CGLib代理的对象不需要实现任何 接口,它是通过动态继承目标对象实现动态代理的。
在上面的main方法加上一行代码,将CGLib代理后的.class文件写入磁盘
public static void main(String[] args) {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"E:\\");
CGLibPerson person = (CGLibPerson) new CGLibProxyPerson().getInstance(CGLibPerson.class);
person.doSomething();
}
对比
- JDK动态代理实现了被代理对象的接口,CGLib代理继承了被代理对象。
- JDK动态代理和CGLib代理都在运行期生成字节码,JDK动态代理直接写Class字节码,CGLib代理使用ASM框架写Class字节码,CGLib代理实现更加复杂,生成代理类比JDK动态代理效率低
- JDK动态代理调用 方法是通过反射机制调用的,CGLib代理是通过FastClass机制直接调用方法的,CGLib代理的执行效率更快
仿写一个JDK动态代理
Person
接口
public interface Person {
String doSomething(String name, Integer age);
void hello(String name);
}
Person
接口实现类
PersonImpl
public class PersonImpl implements Person {
@Override
public String doSomething(String name, Integer age) {
return "name = " + name + ", age = " + age;
}
@Override
public void hello(String name) {
System.out.println("hello name = " + name);
}
}
生成动态代理类
LzcProxy
主要看getInstance方法
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
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.Method;
import java.util.*;
import java.util.stream.Collectors;
public class LzcProxy {
public static final String PROXY_NAME = "$LzcProxy678";
public static final String A = "public final class " + PROXY_NAME + " extends Proxy implements %s {\n\t%s\n}";
public static final String B = "static {\n" +
" try {\n" +
" %s\n" +
" } catch (Exception e) {\n" +
" throw new RuntimeException(e);\n" +
" }\n" +
" }";
// 有参数
public static final String GET_METHOD1 = "Class.forName(\"%s\").getMethod(\"%s\", %s);";
// 无参数
public static final String GET_METHOD2 = "Class.forName(\"%s\").getMethod(\"%s\");";
public static final String METHOD = "\tpublic %s %s(%s) {\n" +
" %s\n" +
" }\n";
public static String THROWABLE = "try {\n" +
" %s\n" +
" } catch (Throwable e) {\n" +
" throw new RuntimeException(e);\n" +
" }";
public static Object getInstance(Class<?>[] interfaces, InvocationHandler handler) {
File sourceFile = null;
try {
// 生成源代码
String sourceCode = generateSourceCode(interfaces);
System.out.println(sourceCode);
// 将源代码写进文件
sourceFile = generateSourceFile(sourceCode);
// 把生成的.java文件编译成.class文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager manage = compiler.getStandardFileManager(null, null, null);
Iterable iterable = manage.getJavaFileObjects(sourceFile);
JavaCompiler.CompilationTask task = compiler.getTask(null, manage, null, null, null, iterable);
task.call();
manage.close();
// 加载类
Class<?> lzcProxy0 = Class.forName(LzcProxy.class.getPackage().getName() + "." + PROXY_NAME);
Constructor<?> constructor = lzcProxy0.getConstructor(InvocationHandler.class);
return constructor.newInstance(handler);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if (sourceFile != null) {
sourceFile.deleteOnExit();
}
}
}
private static File generateSourceFile(String sourceCode) throws IOException {
String filePath = LzcProxy.class.getResource("").getPath();
File file = new File(filePath + PROXY_NAME + ".java");
FileWriter fw = new FileWriter(file);
fw.write(sourceCode);
fw.flush();
fw.close();
return file;
}
// 生成源代码字符串
private static String generateSourceCode(Class<?>[] interfaces) {
// 需要导入的类
Set<String> importClassName = new HashSet<>();
importClassName.add("java.lang.reflect.Proxy");
importClassName.add("java.lang.reflect.InvocationHandler");
importClassName.add("java.lang.reflect.Method");
for (Class clazz : interfaces) {
importClassName.add(clazz.getName());
}
// 开始拼接
StringBuilder sb = new StringBuilder();
// 导入当前包
sb.append("package ").append(LzcProxy.class.getPackage().getName()).append(";\n");
for (String str : importClassName) {
sb.append("import " + str + ";\n");
}
// 实现类字符串
String implementsStr = Arrays.stream(interfaces).map(Class::getSimpleName).collect(Collectors.joining(","));
String content = getContent(interfaces);
sb.append(String.format(A, implementsStr, content));
return sb.toString();
}
private static String getContent(Class<?>[] interfaces) {
StringBuilder sb = new StringBuilder();
// 构造函数
sb.append("public " + PROXY_NAME + "(InvocationHandler var1) {\n\t\tsuper(var1);\n\t}");
Map<String, MethodInfo> methodMap = getMethodMap(interfaces);
// 生成变量
sb.append("\n").append(getMethodField(methodMap));
sb.append("\n").append(getMethod(methodMap));
return sb.toString();
}
// 获取方法信息
private static String getMethod(Map<String, MethodInfo> methodMap) {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, MethodInfo> entry : methodMap.entrySet()) {
MethodInfo methodInfo = entry.getValue();
Method method = methodInfo.getMethod();
// 生成方法参数
Class<?>[] parameterTypes = method.getParameterTypes();
StringBuilder parameterStr = new StringBuilder("");
StringBuilder contentStr = new StringBuilder("");
if (!method.getReturnType().getName().equals("void")) {
contentStr.append("return ").append("(").append(method.getReturnType().getName()).append(")");
}
String[] params = new String[parameterTypes.length];
if (parameterTypes.length > 0) {
int parameterTypeIndex = 0;
for (int i = 0; i < parameterTypes.length; i++) {
if (i > 0) {
parameterStr.append(",");
}
params[i] = "var" + parameterTypeIndex++;
parameterStr.append(parameterTypes[i].getName()).append(" ").append(params[i]);
}
}
contentStr.append("h.invoke(this, ").append(entry.getKey()).append(",");
if (params.length == 0) {
contentStr.append("null");
} else {
contentStr.append("new Object[]{").append(Arrays.stream(params).collect(Collectors.joining(","))).append("}");
}
contentStr.append(");");
sb.append(String.format(METHOD, method.getReturnType().getName(), method.getName(), parameterStr, String.format(THROWABLE, contentStr)));
}
return sb.toString();
}
// 生成变量
private static String getMethodField(Map<String, MethodInfo> methodMap) {
StringBuilder sb = new StringBuilder();
Set<String> methodNameSet = methodMap.keySet();
for (String methodName : methodNameSet) {
sb.append("\tprivate static Method " + methodName + " = null;\n");
}
sb.append(String.format(B, createMethodField(methodMap)));
return sb.toString();
}
// 方法变量实例化
private static String createMethodField(Map<String, MethodInfo> methodMap) {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, MethodInfo> entry : methodMap.entrySet()) {
MethodInfo methodInfo = entry.getValue();
Method method = methodInfo.getMethod();
Class clazz = methodInfo.getClazz();
String str = null;
if (method.getParameterTypes().length == 0) {
str = String.format(GET_METHOD2, clazz.getName(), method.getName());
} else {
String methodFieldList = Arrays.stream(method.getParameterTypes()).map(o -> o.getName() + ".class").collect(Collectors.joining(","));
str = String.format(GET_METHOD1, clazz.getName(), method.getName(), methodFieldList);
}
sb.append(entry.getKey()).append(" = ").append(str).append("\n");
}
return sb.toString();
}
private static TreeMap<String, MethodInfo> getMethodMap(Class<?>[] interfaces) {
TreeMap<String, MethodInfo> methodMap = new TreeMap<>();
int index = 0;
for (Class clazz : interfaces) {
for (Method method : clazz.getMethods()) {
methodMap.put("m" + index++, new MethodInfo(clazz, method));
}
}
return methodMap;
}
}
class MethodInfo {
// 类
private Class clazz;
// 方法
private Method method;
public MethodInfo(Class clazz, Method method) {
this.clazz = clazz;
this.method = method;
}
public Class getClazz() {
return clazz;
}
public void setClazz(Class clazz) {
this.clazz = clazz;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
}
LzcProxyPerson
实现
InvocationHandler
public class LzcProxyPerson implements InvocationHandler {
// 目标对象
private Object target;
public Object getInstance(Object target) {
this.target = target;
return LzcProxy.getInstance(target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis();
Object result = method.invoke(target, args);
long end = System.currentTimeMillis();
System.out.println(method.getName() + " 执行总耗时:" + (end - start));
return result;
}
}
Main方法进行测试
public class Main {
public static void main(String[] args) {
Person person = (Person) new LzcProxyPerson().getInstance(new PersonImpl());
System.out.println("doSomething result = " + person.doSomething("lzc", 20));
person.hello("hahaha");
}
}
版权声明:本文为lizc_lizc原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。