如题,手写一个面向接口的动态代理。我们需要先了解jdk中的动态代理是怎么实现的。
理解生成的代码和调用过程
设置vm参数,-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
,可以使jdk动态生成的class文件输出到磁盘中。
使用下面代码进行调试
public interface IService {
public void service(String name )throws Exception;
}
public class ServiceImplA implements IService {
@Override
public void service(String name) throws Exception {
System.out.println("ServiceImplA name" + name);
}
}
public class DynaProxyServiceA implements InvocationHandler {
private Object object;
/**
* 将目标对象关联到InvocationHandler接口,返回代理对象obj
* 调用代理对象的方法时,都会自动调用invoke方法
*/
public Object bind(Object object){
this.object = object;
return Proxy.newProxyInstance(
this.object.getClass().getClassLoader(),
this.object.getClass().getInterfaces(),
this);
}
@SuppressWarnings("unchecked")
public <T> T bindInterface(Class<T> proxyInterface){
object = proxyInterface;
return (T)Proxy.newProxyInstance(
proxyInterface.getClassLoader(),
new Class[]{proxyInterface},
this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
System.out.println("log start");
if(object instanceof Class<?>){
Class<?> clazz = (Class<?>) object;
for (Method clazzMethod : clazz.getDeclaredMethods()) {
System.out.println(clazzMethod.getName());
}
}
try{
result = method.invoke(this.object,args);
Class<?> returnType = method.getReturnType();
System.out.println(returnType);
}catch (Exception e){
throw e;
}
System.out.println("log end");
return result;
}
public static void main(String [] args) throws Exception {
IService service = (IService)new DynaProxyServiceA()
.bind(new ServiceImplA());
service.service("zhjl");
}
}
输出结果
log start
ServiceImplA namezhjl
void
log end
运行完程序后,会在项目的根目录生成一个文件夹com.sun.proxy
,里面会生成一个$Proxy0.class
的代理类文件。
打开文件可以看到以下生成的源代码。
public final class $Proxy0 extends Proxy implements IService {
private static Method m1;
private static Method m2;
private static Method m3;
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 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 void service(String var1) throws Exception {
try {
super.h.invoke(this, m3, new Object[]{var1});
} catch (Exception | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
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"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("org.example.aop.IService").getMethod("service", Class.forName("java.lang.String"));
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
很容易可以发现,生成的源代码是有规律可寻的。
- 固定会重写
java.lang.Object
中的equals
,toString
,hashCode
三个方法 - 对比静态变量Method的声明对应方法的位置与static代码块这三块地方,可以发现,声明顺序与静态代码块中反射获取的顺序一致,并且在各方法中执行反射出来的Method也是相等的。这里做到了提前加载被代理类的方法,然后使用到该代理类的方法时将被代理类的方法当参数传到
h.invoke
中执行。
进入到被继承的Proxy
中,发现在生成的类中使用的h
就是InvocationHandler
这个类。即我们在使用动态代理时所要实现才那个类!
所以这个地方是生成一个回调代理类的invoke
方法的类,来调用invoke
时决定什么时候执行被代理类的service
方法就能达到切面增强这个方法的效果
理解完了生成代码的意义和处理流程,剩下的就是怎么构造这些代码并将他编译成.class文件
和被类加载器加载并被创建实例被我们所使用了。
如何构造和加载
参考Proxy.newProxyInstance
的代码,看到getProxyClass0
,前面的代码忽略,看注释就知道这里是生成指定代理类的方法。直接点进去就好了。
记住这两个变量分别是KeyFactory
和ProxyClassFactory
。然后回到proxyClassCache.get(loader, interfaces)
根据实现的接口数量来返回Key
往下走,最后指向的类都是Factory
,并在最后执行get
方法。
最后回到ProxyClassFactory
这个类的apply
方法。
最后在方法的底部发现ProxyGenerator.generateProxyClass
对应的作用就是构造相当于构造.java
源文件。defineClass0
相当于javac
编译成.class
文件并loadClass
返回对应的类
这个生成方法较复杂,经过简单的查看源码,已经知道步骤如下:
- 构造
.java
源文件 - 编译成
.class
后加载类
笔者的方法比较简单,直接使用freemaker
来构造源文件,需要传入以下四个参数。
- package->生成类所在的包
- className->生成的代理类名称
- interface->实现的接口类全类名
- methodList->需要重写的方法列表
freemaker模板如下
package ${package};
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
/**
* @Author: Jdragon
* @email: 1061917196@qq.com
* @Description: jdk动态代理实质
*/
public class ${className} extends Proxy implements ${interface} {
public ${className}(InvocationHandler h) {
super(h);
}
<#list 0..(methodList!?size-1) as i>
@Override
public final ${methodList[i].retType} ${methodList[i].methodName}(
<#list methodList[i].paramList as param>
${param} var${param_index}<#if param_has_next>,</#if>
</#list>) {
try {
<#if (methodList[i].retType!="void")>return (${methodList[i].retType})</#if>
<#if (methodList[i].paramList?size==0)>
super.h.invoke(this, m${i}, (Object[])null);
<#else>
super.h.invoke(this, m${i}, new Object[]{
<#list 0..(methodList[i].paramList!?size-1) as k>var${k}
<#if k_has_next>,</#if>
</#list>});
</#if>
} catch (RuntimeException | Error e) {
throw e;
}catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
</#list>
<#list 0..(methodList!?size-1) as i>
private static Method m${i};
</#list>
static{
try{
<#list 0..(methodList!?size-1) as i>
m${i} = Class.forName("${methodList[i].className}").getMethod("${methodList[i].methodName}"
<#list methodList[i].paramList as param>
,Class.forName("${param}")
</#list>);
</#list>
} catch (NoSuchMethodException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
主要代码。以下代码不完整,可到gitee获取源码
public class JdkProxyFactory {
private final static String LN = System.lineSeparator();
private final static AtomicInteger PROXY_INDEX = new AtomicInteger(0);
private final static Boolean SAVE_GENERATED_FILES = Boolean.valueOf(System.getProperty("sun.misc.ProxyGenerator.saveGeneratedFiles"));
private final static String USER_DIR = System.getProperty("user.dir") + "/com/jdragon/proxy/";
private final static String PACKAGE_NAME = "com.jdragon.proxy";
public static Object newProxyInstance(ClassLoader classLoader,
@NotNull Class<?>[] interfaces,
@NotNull InvocationHandler h) {
try {
if (interfaces.length == 0) {
throw new Exception("至少要实现一个接口");
}
//使用被代理类的类名和自增数定义代理类的名字
String proxyClass = interfaces[0].getSimpleName() + "$Proxy" + PROXY_INDEX.incrementAndGet();
//加载代理类
Class<?> loadClass = loadClass(interfaces[0], proxyClass);
Constructor<?> constructor = loadClass.getDeclaredConstructor(InvocationHandler.class);
return constructor.newInstance(h);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* @Description: 加载类
**/
private static Class<?> loadClass(Class<?> interfaces, String proxyClassName) throws Exception {
String classPath = PACKAGE_NAME + "." + proxyClassName;
//构建源代码
String sourceCode = generateSourceCode(interfaces, proxyClassName);
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
try (JavaFileManager manager = new MemoryFileManager(compiler.getStandardFileManager(null, null, null))) {
List<JavaFileObject> files = Collections.singletonList(new MemoryJavaFileObject(proxyClassName, sourceCode));
JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, files);
if (!task.call()) {
throw new Exception("任务调用异常");
}
ClassLoader classLoader = manager.getClassLoader(null);
Class<?> aClass = manager.getClassLoader(null).loadClass(classPath);
if (SAVE_GENERATED_FILES) {
save(proxyClassName, classLoader);
}
return aClass;
}
}
/**
* @Description: 构造源代码
**/
private static String generateSourceCode(Class<?> interfaces, String proxyClassName) {
String interfaceName = interfaces.getName();
List<MethodEntity> methodEntities = new ArrayList<>();
methodEntities.add(new MethodEntity(Object.class, "toString", null,
String.class));
methodEntities.add(new MethodEntity(Object.class, "hashCode", null,
int.class));
methodEntities.add(new MethodEntity(Object.class, "equals", Collections.singletonList(Object.class.getName()),
boolean.class));
for (Method declaredMethod : interfaces.getDeclaredMethods()) {
MethodEntity methodEntity = new MethodEntity();
methodEntity.setClassName(interfaces);
methodEntity.setMethodName(declaredMethod.getName());
List<String> params = new ArrayList<>();
for (Parameter parameter : declaredMethod.getParameters()) {
String paramTypeName = parameter.getType().getName();
params.add(paramTypeName);
}
methodEntity.setParamList(params);
methodEntity.setRetType(declaredMethod.getReturnType());
methodEntity.setTransferType(declaredMethod.getReturnType());
methodEntities.add(methodEntity);
}
//利用定义好的模板传入参数到freemaker进行遍历填充,最后获得源代码
Map<String, Object> map = new HashMap<>(8);
map.put("package", PACKAGE_NAME);
map.put("className", proxyClassName);
map.put("interface", interfaceName);
map.put("methodList", methodEntities);
FreeMakerUtil freeMakerUtil = new FreeMakerUtil("/template/freemaker/", "ftl");
return freeMakerUtil.printString("proxy", map);
}
}