如題威蕉,手寫(xiě)一個(gè)面向接口的動(dòng)態(tài)代理畜埋。我們需要先了解jdk中的動(dòng)態(tài)代理是怎么實(shí)現(xiàn)的望众。
理解生成的代碼和調(diào)用過(guò)程
設(shè)置vm參數(shù),-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
溃卡,可以使jdk動(dòng)態(tài)生成的class文件輸出到磁盤(pán)中。
使用下面代碼進(jìn)行調(diào)試
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;
/**
* 將目標(biāo)對(duì)象關(guān)聯(lián)到InvocationHandler接口蜒简,返回代理對(duì)象obj
* 調(diào)用代理對(duì)象的方法時(shí)瘸羡,都會(huì)自動(dòng)調(diào)用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");
}
}
輸出結(jié)果
log start
ServiceImplA namezhjl
void
log end
運(yùn)行完程序后,會(huì)在項(xiàng)目的根目錄生成一個(gè)文件夾com.sun.proxy
搓茬,里面會(huì)生成一個(gè)$Proxy0.class
的代理類(lèi)文件犹赖。
打開(kāi)文件可以看到以下生成的源代碼。
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());
}
}
}
很容易可以發(fā)現(xiàn)垮兑,生成的源代碼是有規(guī)律可尋的冷尉。
- 固定會(huì)重寫(xiě)
java.lang.Object
中的equals
,toString
系枪,hashCode
三個(gè)方法 - 對(duì)比靜態(tài)變量Method的聲明對(duì)應(yīng)方法的位置與static代碼塊這三塊地方雀哨,可以發(fā)現(xiàn),聲明順序與靜態(tài)代碼塊中反射獲取的順序一致私爷,并且在各方法中執(zhí)行反射出來(lái)的Method也是相等的雾棺。這里做到了提前加載被代理類(lèi)的方法,然后使用到該代理類(lèi)的方法時(shí)將被代理類(lèi)的方法當(dāng)參數(shù)傳到
h.invoke
中執(zhí)行衬浑。
進(jìn)入到被繼承的Proxy
中捌浩,發(fā)現(xiàn)在生成的類(lèi)中使用的h
就是InvocationHandler
這個(gè)類(lèi)。即我們?cè)谑褂脛?dòng)態(tài)代理時(shí)所要實(shí)現(xiàn)才那個(gè)類(lèi)!
所以這個(gè)地方是生成一個(gè)回調(diào)代理類(lèi)的invoke
方法的類(lèi)工秩,來(lái)調(diào)用invoke
時(shí)決定什么時(shí)候執(zhí)行被代理類(lèi)的service
方法就能達(dá)到切面增強(qiáng)這個(gè)方法的效果
理解完了生成代碼的意義和處理流程尸饺,剩下的就是怎么構(gòu)造這些代碼并將他編譯成.class文件
和被類(lèi)加載器加載并被創(chuàng)建實(shí)例被我們所使用了。
如何構(gòu)造和加載
參考Proxy.newProxyInstance
的代碼助币,看到getProxyClass0
,前面的代碼忽略浪听,看注釋就知道這里是生成指定代理類(lèi)的方法。直接點(diǎn)進(jìn)去就好了眉菱。
記住這兩個(gè)變量分別是KeyFactory
和ProxyClassFactory
迹栓。然后回到proxyClassCache.get(loader, interfaces)
根據(jù)實(shí)現(xiàn)的接口數(shù)量來(lái)返回Key
往下走,最后指向的類(lèi)都是Factory
俭缓,并在最后執(zhí)行get
方法克伊。
最后回到ProxyClassFactory
這個(gè)類(lèi)的apply
方法酥郭。
最后在方法的底部發(fā)現(xiàn)ProxyGenerator.generateProxyClass
對(duì)應(yīng)的作用就是構(gòu)造相當(dāng)于構(gòu)造.java
源文件。
defineClass0
相當(dāng)于javac
編譯成.class
文件并loadClass
返回對(duì)應(yīng)的類(lèi)
這個(gè)生成方法較復(fù)雜愿吹,經(jīng)過(guò)簡(jiǎn)單的查看源碼不从,已經(jīng)知道步驟如下:
- 構(gòu)造
.java
源文件 - 編譯成
.class
后加載類(lèi)
筆者的方法比較簡(jiǎn)單,直接使用freemaker
來(lái)構(gòu)造源文件洗搂,需要傳入以下四個(gè)參數(shù)消返。
- package->生成類(lèi)所在的包
- className->生成的代理類(lèi)名稱
- interface->實(shí)現(xiàn)的接口類(lèi)全類(lèi)名
- methodList->需要重寫(xiě)的方法列表
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動(dòng)態(tài)代理實(shí)質(zhì)
*/
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("至少要實(shí)現(xiàn)一個(gè)接口");
}
//使用被代理類(lèi)的類(lèi)名和自增數(shù)定義代理類(lèi)的名字
String proxyClass = interfaces[0].getSimpleName() + "$Proxy" + PROXY_INDEX.incrementAndGet();
//加載代理類(lèi)
Class<?> loadClass = loadClass(interfaces[0], proxyClass);
Constructor<?> constructor = loadClass.getDeclaredConstructor(InvocationHandler.class);
return constructor.newInstance(h);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* @Description: 加載類(lèi)
**/
private static Class<?> loadClass(Class<?> interfaces, String proxyClassName) throws Exception {
String classPath = PACKAGE_NAME + "." + proxyClassName;
//構(gòu)建源代碼
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("任務(wù)調(diào)用異常");
}
ClassLoader classLoader = manager.getClassLoader(null);
Class<?> aClass = manager.getClassLoader(null).loadClass(classPath);
if (SAVE_GENERATED_FILES) {
save(proxyClassName, classLoader);
}
return aClass;
}
}
/**
* @Description: 構(gòu)造源代碼
**/
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);
}
//利用定義好的模板傳入?yún)?shù)到freemaker進(jìn)行遍歷填充撵颊,最后獲得源代碼
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);
}
}