1、手动创建handle接口
public interface InterfaceHandle {public Object invoke(Method method, Object[] args);
}
2、实现handle接口
public class User_InvoctionHandle implements InterfaceHandle {private Object target;public User_InvoctionHandle(Object target) {this.target = target;}public Object invoke(Method method, Object[] args) {System.out.println("我是自定义invoke方法");try {return method.invoke(this.target, null);} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}return null;}
}
3、手写代理类(附带逻辑重写)
public class ProxyUtil {public static Object getInstance(Class targetInfo, User_InvoctionHandle handle) {Object proxy = null;
// Class targetInfo = target.getClass().getInterfaces()[0];String tab = "\t";String line = "\n";String implName = targetInfo.getSimpleName();//创建java内容String javaContent = "";//packageString packageContent = "package com.proxy;" + line;//importClassString impPackageContent = "import "+handle.getClass().getName()+";"+line+"import java.lang.reflect.Method;"+line+"import "+targetInfo.getName()+";"+line;//创建类体String classContent = "public class $Proxy implements " + implName + " {" + line;//创建私有对象handleString privateObject = tab + "private " + handle.getClass().getSimpleName() + " h;" + line;//创建构造,将handle当做对象String constructorContent = tab + "public $Proxy (" + handle.getClass().getSimpleName() + " h ){" + line;constructorContent += tab + tab + "this.h = h;";constructorContent += line + tab + "}" + line;//创建方法String methedContent = "";Method[] methods = targetInfo.getDeclaredMethods();for (Method method : methods) {//获取方法的返回类型String methodTypeName = method.getReturnType().getSimpleName();//获取方法的名字String methodName = method.getName();methedContent += tab + "public " + methodTypeName + " " + methodName + " (";//创建参数Object[] args = method.getParameterTypes();String argContent = "";for (int i = 0; i
4、测试
public static void main(String args[]) {//调用手动写的代理ObjectDao objectDao = (ObjectDao) ProxyUtil.getInstance(ObjectDao.class, new User_InvoctionHandle(new User_Defined_Dao()));System.out.println(objectDao.queryStr());}
5、测试重生成的$Proxy代理类及class
自动生成代理类的编码
package com.dao;
import com.handle.User_InvoctionHandle;
import java.lang.reflect.Method;
import com.dao.ObjectDao;
public class $Proxy implements ObjectDao {private User_InvoctionHandle h;public $Proxy (User_InvoctionHandle h ){this.h = h;}public void query (){try{Method method = Class.forName("com.dao.ObjectDao").getDeclaredMethod("query");h.invoke(method,null);}catch(Exception e){e.printStackTrace();}}public String queryStr (){try{Method method = Class.forName("com.dao.ObjectDao").getDeclaredMethod("queryStr");return (String)h.invoke(method,null);}catch(Exception e){e.printStackTrace();}return null;}}
结果
手写带参数动态代理
package com.proxy;/** 对象是如何生成的?* java* class* new* **/import com.handle.User_InvoctionHandle;import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.net.URL;
import java.net.URLClassLoader;public class ProxyUtil {public static Object getInstance(Class targetInfo, User_InvoctionHandle handle) {Object proxy = null;
// Class targetInfo = target.getClass().getInterfaces()[0];String tab = "\t";String line = "\n";String implName = targetInfo.getSimpleName();//创建java内容String javaContent = "";//packageString packageContent = "package com.proxy;" + line;//importClassString impPackageContent = "import "+handle.getClass().getName()+";"+line+"import java.lang.reflect.Method;"+line+"import "+targetInfo.getName()+";"+line;//创建类体String classContent = "public class $Proxy implements " + implName + " {" + line;//创建私有对象handleString privateHandle = tab + "private " + handle.getClass().getSimpleName() + " h;" + line;//创建构造,将handle当做对象String constructorContent = tab + "public $Proxy (" + handle.getClass().getSimpleName() + " h ){" + line;constructorContent += tab + tab + "this.h = h;";constructorContent += line + tab + "}" + line;//创建方法String methedContent = "";Method[] methods = targetInfo.getDeclaredMethods();for (Method method : methods) {//获取方法的返回类型String methodTypeName = method.getReturnType().getSimpleName();//获取方法的名字String methodName = method.getName();methedContent += tab + "public " + methodTypeName + " " + methodName + " (";//创建参数Object[] args = method.getParameterTypes();String argContent = "";String argClassContent = "";String argValue = "Object [] objects = new Object[]{";for (int i = 0; i
}
自定义写的动态代理缺点:
需要生成文件
动态编译文件
IO操作
需要一个URLClassloader
性能问题
public static Object newProxyInstance(ClassLoader loader,Class>[] interfaces,InvocationHandler h)throws IllegalArgumentException{Objects.requireNonNull(h);final Class>[] intfs = interfaces.clone();final SecurityManager sm = System.getSecurityManager();if (sm != null) {checkProxyAccess(Reflection.getCallerClass(), loader, intfs);}/**查找或生成指定的代理类 这里返回的是查看代理缓存里面是否存在同一个类加载器加载的接口 return proxyClassCache.get(loader,interfaces)
*扩展,判断两个对象是否相同,首先先要判断是否是同一个类加载器加载进来的对象。(类似两个不同的公司【代表类加载器】,就算部门【对象】一致也不能说这两个部门是同一部门)
*从这段代码中,我们可以看出jkd代理,底层是存在缓存的,也就是当jdk每次代理一个接口时,已经会在完成前先存入缓存,方便以后使用。(可想而知,高级开发[规范有必要]中一定应该想到性能的问题)
**/
Class> cl = getProxyClass0(loader, intfs);/** 使用指定的调用处理程序调用其构造函数。*/try {if (sm != null) {checkNewProxyPermission(Reflection.getCallerClass(), cl);}//调用静态构造器final Constructor> cons = cl.getConstructor(constructorParams);final InvocationHandler ih = h;
//判断这个方法的修饰是否为public(很关键,因为这个设计到相关包的问题,如果不是public则不是同包的情况下不能引包)if (!Modifier.isPublic(cl.getModifiers())) {AccessController.doPrivileged(new PrivilegedAction
//这里的cons就是代理器的实体对象,里面包含了目标对象,非常重要return cons.newInstance(new Object[]{h});} catch (IllegalAccessException|InstantiationException e) {throw new InternalError(e.toString(), e);} catch (InvocationTargetException e) {Throwable t = e.getCause();if (t instanceof RuntimeException) {throw (RuntimeException) t;} else {throw new InternalError(t.toString(), t);}} catch (NoSuchMethodException e) {throw new InternalError(e.toString(), e);}}
从上述方法中可以看出Constructor获取的对象cons很重要,因为从此代码往下就是直接使用并获取InvocationHandle对象了,下面深入了解一下当中的构造类的构造对象如何获取
final Constructor> cons = cl.getConstructor(constructorParams);
此方法就是从缓存中获取代理对象,如果没有缓存则使用其他方式获取代理对象,请看以下代码
如何看源码?在这里叫大家一个比较使用的方式,直接找到返回值查看这个返回值是否是想要的,如果是则查看生成返回值得地方就可以
public V get(K key, P parameter) {Objects.requireNonNull(parameter);expungeStaleEntries();Object cacheKey &#61; CacheKey.valueOf(key, refQueue);// 懒加载cacheKey的第2级valuesMapConcurrentMap> valuesMap &#61; map.get(cacheKey);if (valuesMap &#61;&#61; null) {ConcurrentMap> oldValuesMap&#61; map.putIfAbsent(cacheKey,valuesMap &#61; new ConcurrentHashMap<>());if (oldValuesMap !&#61; null) {valuesMap &#61; oldValuesMap;}}Object subKey &#61; Objects.requireNonNull(subKeyFactory.apply(key, parameter));Supplier
以上代码中我们得到的就是value 而这个value就是我们想要的$Proxy对象
根据上述截图将断点设置在value&#61;supplier.get();并F5跟进去
根据以上方法我们最终进入到Proxy类中的内部类
&#64;Overridepublic Class> apply(ClassLoader loader, Class>[] interfaces) {Map
使用byte[] proxyClassFile &#61; ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);生成jdk代理类.class文件
//获取JDK生成的代理对象.Class文件byte bytes[] &#61;ProxyGenerator.generateProxyClass("Proxy_wjw",new Class []{(ObjectDao.class)});//为了方便直接在方法中throws ExceptionFileOutputStream fileOutputStream &#61; new FileOutputStream("D:\\$Proxy_JDK.class");fileOutputStream.write(bytes);fileOutputStream.flush();fileOutputStream.close();
$Proxy_JDK.class文件反编译后
pa
package com.handle;
import com.dao.ObjectDao;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy_JDK extends Proxy implements ObjectDao {
private static Method m1;
private static Method m4;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy_JDK(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 queryStr(String var1) throws {
try {
return (String)super.h.invoke(this, m4, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void query() 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 &#61; Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m4 &#61; Class.forName("com.dao.ObjectDao").getMethod("queryStr", Class.forName("java.lang.String"));
m3 &#61; Class.forName("com.dao.ObjectDao").getMethod("query");
m2 &#61; Class.forName("java.lang.Object").getMethod("toString");
m0 &#61; Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
}
区别就是多了一个继承和几个方法&#xff08;equals、toString、hashCOde&#xff09;
defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);此方法是JDK将bety数组转换成对象的底层方法
总结&#xff1a;通过接口反射成字节码&#xff0c;然后把字节码转换成class&#xff0c;再通过native方法转成对象
遗留疑问&#xff1a;JDK代理和cglib代理的区别&#xff1f;spring底层就是用的cglib&#xff0c;预知后续请持续关注