本文共 23126 字,大约阅读时间需要 77 分钟。
# 增强一个对象的功能# 买火车票,App 就是一个代理,他代理了火车站,小区当中的代售窗口# Java 当中如何实现代理
代理对象 ===> 增强后的对象目标对象 ===> 被增强的对象他们不是绝对的,会根据情况发生变化
1、继承代理对象继承目标对象,重写需要增强的方法;缺点:会代理类过多,非常复杂2、聚合目标对象和代理对象实现同一个接口,代理对象当中要包含目标对象。缺点:也会产生类爆炸,只不过比继承少一点点# 总结:如果在不确定的情况下,尽量不要去使用静态代理。因为一旦你写代码,就会产生类,一旦产生类就爆炸。
自己模拟 JDK 动态代理
# 不需要订阅专栏手动创建类文件(因为一旦手动创建类文件,就会产生类爆炸),通过接口反射生成一个类文件,然后调用第三方的编译技术,动态编译这个产生的类文件成class文件,继而利用UrlclassLoader(因为这个动态产生的class不在工程当中所以需要使用UrlclassLoader)把这个动态编译的类加载到jvm当中,最后通过反射把这个类实例化。缺点:首先要生成文件缺点:动态编译class文件缺点:需要一个 URLclassloader软件性能的最终体现在 IO 操作
接口:FutureDao
/** * @description: 接口 * @author: Mr.Li * @date: Created in 2020/7/1 15:42 * @version: 1.0 * @modified By: */public interface FutureDao { public void query(); public String returnString();}
实现类:FutureDaoImpl
/** * @description: 实现类 * @author: Mr.Li * @date: Created in 2020/7/1 15:43 * @version: 1.0 * @modified By: */public class FutureDaoImpl implements FutureDao { @Override public void query(){ System.out.println("FutureLL"); } @Override public String returnString() { return "Return String"; }}
自己实现动态代理:ProxyUtil
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.net.URL;import java.net.URLClassLoader;/** * @description: 自己实现动态代理 * @author: Mr.Li * @date: Created in 2020/7/1 13:19 * @version: 1.0 * @modified By: */public class ProxyUtil { /** * content --->string * .java io * .class ** .new 反射----》class * * @return */ public static Object newInstance(Object target) { // 定义代理对象 Object proxy = null; // getClass(): Returns the runtime class of this {@code Object}. // getInterfaces(): Determines the interfaces implemented by the class or interface represented by this object. Class targetInf = target.getClass().getInterfaces()[0]; Method methods[] = targetInf.getDeclaredMethods(); String line = "\n"; String tab = "\t"; // getSimpleName(): Returns the simple name of the underlying class as given in the source code. // infName = "FutureDao"; String infName = targetInf.getSimpleName(); String content = ""; String packageContent = "package com.google;" + line; // Returns the name of the entity represented by this {@code Class} object, as a {@code String}. // targetInf.getName() = "com.futurell.dao.FutureDao" String importContent = "import " + targetInf.getName() + ";" + line; String clazzFirstLineContent = "public class $Proxy implements " + infName + "{" + line; String filedContent = tab + "private " + infName + " target;" + line; String constructorContent = tab + "public $Proxy (" + infName + " target){" + line + tab + tab + "this.target = target;" + line + tab + "}" + line; String methodContent = ""; for (Method method : methods) { // getReturnType(): Returns a Class object that represents the formal return type of the method represented by this {@code Method} object. String returnTypeName = method.getReturnType().getSimpleName(); // getName(): Returns the name of the method represented by this {@code Method} object, as a {@code String}. String methodName = method.getName(); // Sting.class String.class Class args[] = method.getParameterTypes(); String argsContent = ""; String paramsContent = ""; int flag = 0; for (Class arg : args) { //getSimpleName(): Returns the simple name of the underlying class as given in the source code. String temp = arg.getSimpleName(); // String // String p0,Sting p1, argsContent += temp + " p" + flag + ","; paramsContent += "p" + flag + ","; flag ++; } if (argsContent.length() > 0) { // lastIndexOf(): Returns the index within this string of the last occurrence of the specified substring. argsContent = argsContent.substring(0, argsContent.lastIndexOf(",") - 1); paramsContent = paramsContent.substring(0, paramsContent.lastIndexOf(",") - 1); } methodContent += tab + "public " + returnTypeName + " " + methodName + "(" + argsContent + ") {" + line + tab + tab + "System.out.println(\" Log \");" + line; if (returnTypeName.equals("void")) { methodContent += tab + tab + "target." + methodName + "(" + paramsContent + ");" + line + tab + "}" + line; } else { methodContent += tab + tab + "return target." + methodName + "(" + paramsContent + ");" + line + tab + "}" + line; } } content = packageContent + importContent + clazzFirstLineContent + filedContent + constructorContent + methodContent + "}"; File file = new File("e:\\com\\google\\$Proxy.java"); try { if (!file.exists()) { file.createNewFile(); } FileWriter fw = new FileWriter(file); fw.write(content); fw.flush(); fw.close(); /** * 编译过程如下: */ // getSystemJavaCompiler(): Gets the Java; programming language compiler provided with this platform. // 得到编译类,可以动态的编译一些文件 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); // getStandardFileManager(): Gets a new instance of the standard file manager implementation for this tool. // 编译文件需要文件管理器 StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null); // getJavaFileObjects(): Gets file objects representing the given files. // 把文件放到文件管理器中 Iterable units = fileMgr.getJavaFileObjects(file); // getTask(): Creates a future for a compilation task with the given components and arguments. // JavaCompiler.CompilationTask: Interface representing a future for a compilation task. // 把文件管理器当成一个任务来执行 JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units); // call(): Performs this compilation task. t.call(); fileMgr.close(); // URL[]: Class {@code URL} represents a Uniform Resource Locator, a pointer to a "resource" on the World Wide Web. URL[] urls = new URL[]{new URL("file:E:\\\\")}; // URLClassLoader: This class loader(类加载器) is used to load classes and resources from a search path of URLs // referring(引用) to both JAR files(Java Archive, Java归档) and directories(目录). // JAR: 通常用于聚合大量的Java类文件、相关的元数据和资源(文本、图片等)文件到一个文件,以便开发Java平台应用软件或库。 URLClassLoader urlClassLoader = new URLClassLoader(urls); // loadClass(): Loads the class with the specified binary name. Class clazz = urlClassLoader.loadClass("com.google.$Proxy"); // getConstructor(): Returns a {@code Constructor} object that reflects(反应) the specified // public constructor of the class represented by this {@code Class} object. // clazz这个类它有构造方法,只能以构造方法来new这个类的实例,所以需要先得到该类的构造方法 Constructor constructor = clazz.getConstructor(targetInf); // newInstance(): Uses the constructor represented by this {@code Constructor} object to // create and initialize a new instance of the constructor's // declaring class, with the specified initialization parameters. // 通过构造方法来创建实例 proxy = constructor.newInstance(target); // clazz.newInstance(); // Class.forName() } catch (Exception e) { e.printStackTrace(); } /** * public UserDaoLog(UserDao target){ * this.target = target; * } */ return proxy; }}
测试类:Test
import com.futurell.dao.FutureDao;import com.futurell.dao.FutureDaoImpl;import com.futurell.proxy.ProxyUtil;/** * @description: 测试 * @author: Mr.Li * @date: Created in 2020/7/1 10:56 * @version: 1.0 * @modified By: */public class Test { public static void main(String[] args) { FutureDao fProxy = (FutureDao) ProxyUtil.newInstance(new FutureDaoImpl()); fProxy.query(); System.out.println("------------------"); FutureDao fProxyString = (FutureDao) ProxyUtil.newInstance(new FutureDaoImpl()); System.out.println(fProxyString.returnString()); }}/** * 输出: * ------------------ * Log * FutureLL * ------------------ * Log * Return String */
JDK 动态代理
接口和实现类与上述自定义相同
代理类:FutureInvocationHandler
import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;/** * @description: * @author: Mr.Li * @date: Created in 2020/7/2 12:24 * @version: 1.0 * @modified By: */public class FutureInvocationHandler implements InvocationHandler { Object target; public FutureInvocationHandler(Object target) { this.target = target; } /** * * @param proxy 代理对象 * @param method 代理对象包含目标对象,目标对象中的方法,就是method * @param args 目标方法的参数 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("proxy"); return method.invoke(target, args); }}
测试类:Test
import com.futurell.JDK.dao.FutureDao;import com.futurell.JDK.dao.FutureDaoImpl;import com.futurell.JDK.proxy.FutureInvocationHandler;import java.lang.reflect.Proxy;/** * @description: 测试 * @author: Mr.Li * @date: Created in 2020/7/1 10:56 * @version: 1.0 * @modified By: */public class Test { public static void main(String[] args) { FutureDao jdkProxy = (FutureDao) Proxy.newProxyInstance( // getClassLoader(): Returns the class loader for the class. // 判断一个类是否相同,需要根据类加载器是否相同来判断,为了保证加载器可用,那么传入当前所在类的ClassLoader // 在自定义动态代理的代码中,使用了URLClassLoader,因为自定义的类不在工程当中 Test.class.getClassLoader(), // Class targetInf = target.getClass().getInterfaces()[0]; // 自定义使用了上边的代码,我们需要得到接口,用来得到接口中的方法,然后对方法进行代理 new Class[]{FutureDao.class}, new FutureInvocationHandler(new FutureDaoImpl())); jdkProxy.query(); System.out.println("--------------"); System.out.println(jdkProxy.returnString()); }}
CGLIB
如果是要实现 CGLIB,需要先添加 cglib 依赖,然后实现一个接口 MethodInterceptor,重写 intercept() 方法
依赖
cglib cglib 3.3.0 compile
代理类:MyMethodInterceptor
import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * 自定义MethodInterceptor */public class MyMethodInterceptor implements MethodInterceptor{ /** * sub:cglib生成的代理对象 * method:被代理对象方法 * objects:方法入参 * methodProxy: 代理方法 */ @Override public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("======插入前置通知======"); Object object = methodProxy.invokeSuper(sub, objects); System.out.println("======插入后者通知======"); return object; }}
JDK 底层原理
测试类:Test
FutureDao jdkProxy = (FutureDao) Proxy.newProxyInstance( // getClassLoader(): Returns the class loader for the class. // 判断一个类是否相同,需要根据类加载器是否相同来判断,为了保证加载器可用,那么传入当前所在类的ClassLoader // 在自定义动态代理的代码中,使用了URLClassLoader,因为自定义的类不在工程当中 Test.class.getClassLoader(), // Class targetInf = target.getClass().getInterfaces()[0]; // 自定义使用了上边的代码,我们需要得到接口,用来得到接口中的方法,然后对方法进行代理 new Class[]{FutureDao.class}, new FutureInvocationHandler(new FutureDaoImpl()));
测试类调用 Proxy 类的 newProxyInstance() 方法
/** * Returns an instance of a proxy class for the specified interfaces * that dispatches method invocations to the specified invocation * handler. */@CallerSensitivepublic static Object newProxyInstance(ClassLoader loader, Class [] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); // 拿到实现类的接口,使用接口可以对接口中的方法进行处理 final Class [] intfs = interfaces.clone(); // 系统做的安全验证,这个不需要我们去管 // getSecurityManager(): Gets the system security interface. final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * Look up or generate the designated proxy class. * 找到或生成指定的代理类 */ Class cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } // 通过 cl.getConstructor() 得到一个构造方法 final Constructor cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction() { public Void run() { cons.setAccessible(true); return null; } }); } // 这里将得到的构造方法 new 成一个对象 // 所以我们需要看这里 new 出来的对象是不是我们需要的代理对象 // 首先复制 cons.newInstance(new Object[]{h}),鼠标右键点击 Evaluate Expression,将复制的代码粘贴回车,看到 result 的值就是我们的代理对象 // 得到 cl 代理类,代理对象就轻易被 new 出来了,在我们的传入的是 new FutureInvocationHandler(new FutureDaoImpl()) // 所以最重要的是我们怎么得到的 cl 代理类,进入 getProxyClass0() 这个方法 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); }}
进入 getProxyClass0() 我们要知道如何得到的 cl 代理类
/** * Generate a proxy class. Must call the checkProxyAccess method * to perform permission checks before calling this. */private static Class getProxyClass0(ClassLoader loader, Class ... interfaces) { if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } // If the proxy class defined by the given loader implementing // the given interfaces exists, this will simply return the cached copy; // otherwise, it will create the proxy class via the ProxyClassFactory // 如果实现给定接口的给定加载器定义的代理类存在,这将简单地返回缓存的副本;否则,它将通过 ProxyClassFactory 创建代理类 // 这个方法只有一行代码,我们进入 get() 方法 return proxyClassCache.get(loader, interfaces);}
进入 get() 方法
/** * Look-up the value through the cache. This always evaluates the * {@code subKeyFactory} function and optionally evaluates * {@code valueFactory} function if there is no entry in the cache for given * pair of (key, subKey) or the entry has already been cleared. */public V get(K key, P parameter) { Objects.requireNonNull(parameter); expungeStaleEntries(); Object cacheKey = CacheKey.valueOf(key, refQueue); // lazily install the 2nd level valuesMap for the particular cacheKey ConcurrentMap
进入另一个 get() 方法
@Overridepublic synchronized V get() { // serialize access // re-check Suppliersupplier = valuesMap.get(subKey); if (supplier != this) { // something changed while we were waiting: // might be that we were replaced by a CacheValue // or were removed because of failure -> // return null to signal WeakCache.get() to retry // the loop return null; } // else still us (supplier == this) // create new value // 这个方法我们可以看出,结果需要返回 value,而 value 最终的取值是在下边的 try-finally 中取值 V value = null; try { // 首先我们进入 apply() 方法 value = Objects.requireNonNull(valueFactory.apply(key, parameter)); } finally { if (value == null) { // remove us on failure valuesMap.remove(subKey, this); } } // the only path to reach here is with non-null value assert value != null; // wrap value with CacheValue (WeakReference) CacheValue cacheValue = new CacheValue<>(value); // put into reverseMap reverseMap.put(cacheValue, Boolean.TRUE); // try replacing us with CacheValue (this should always succeed) if (!valuesMap.replace(subKey, this, cacheValue)) { throw new AssertionError("Should not reach here"); } // successfully replaced us with new CacheValue -> return the value // wrapped by it // 返回 value return value;}
进入 apply() 方法
@Overridepublic Class apply(ClassLoader loader, Class [] interfaces) { Map, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); // 这里的 interfaces 的所有接口只有一个 FutureDao,循环所有接口,将接口赋给 intf for (Class intf : interfaces) { /* * Verify that the class loader resolves the name of this * interface to the same Class object. */ Class interfaceClass = null; try { // 这个有个点需要注意, intf 本身就是一个 Class,为什么这里还要再次得到一个 Class // 判断对象是否相同的一个前提,是否是同一个类加载器 // 原因: 这里要判断两个接口是不是同一个接口, interfaceClass = Class.forName(intf.getName(), false, loader); } catch (ClassNotFoundException e) { } // 如果两个不相同那么抛出异常: IllegalArgumentException,相等继续执行后面的代码 if (interfaceClass != intf) { throw new IllegalArgumentException( intf + " is not visible from class loader"); } /* * Verify that the Class object actually represents an * interface. */ if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } /* * Verify that this interface is not a duplicate. */ if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } } String proxyPkg = null; // package to define proxy class in int accessFlags = Modifier.PUBLIC | Modifier.FINAL; /* * Record the package of a non-public proxy interface so that the * proxy class will be defined in the same package. Verify that * all non-public proxy interfaces are in the same package. */ for (Class intf : interfaces) { int flags = intf.getModifiers(); // 如果这个接口不是 public,那么引用的时候会有问题,这里需要做一个转换 if (!Modifier.isPublic(flags)) { accessFlags = Modifier.FINAL; String name = intf.getName(); int n = name.lastIndexOf('.'); String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); if (proxyPkg == null) { proxyPkg = pkg; } else if (!pkg.equals(proxyPkg)) { throw new IllegalArgumentException( "non-public interfaces from different packages"); } } } if (proxyPkg == null) { // if no non-public proxy interfaces, use com.sun.proxy package proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } /* * Choose a name for the proxy class to generate. */ long num = nextUniqueNumber.getAndIncrement(); // 这里给 proxyPkg 加一个标识,防止并发的情况下类名相同 String proxyName = proxyPkg + proxyClassNamePrefix + num; /* * Generate the specified proxy class. * 生成指定的代理类的二进制文件, */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags); try { // 我们知道一个对象的产生顺序: // .java ---> .class ---ClassLoader---> JVM ---byte[]---> object // 而这里需要返回一个 Class,那么将二进制文件转换成对象就在 defineClass0() 方法中进行 return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { /* * A ClassFormatError here means that (barring bugs in the * proxy class generation code) there was some other * invalid aspect of the arguments supplied to the proxy * class creation (such as virtual machine limitations * exceeded). */ throw new IllegalArgumentException(e.toString()); }}
进入 defineClass0() 方法
// 这个方法是一个被 native 修饰的方法是一个本地方法,也就是一个 Java 调用非 Java 代码的接口,再开发 JDK 的时候底层使用了 C 或 C++ 开发(可以去看一下 OpenJDK),也就是说 C 和 C++ 负责把 byte[] 变成 Class 对象private static native Class defineClass0(ClassLoader loader, String name, byte[] b, int off, int len);
总结:
# 通过接口反射得到字节码,然后把字节码转换成 Class,就得到了我们想要的对象
有兴趣的同学可以关注我的个人公众号,期待我们共同进步!!!
转载地址:http://qxzad.baihongyu.com/