代理模式的理解
什么是代理?
在Design patterns In java这个本书中是这样描述的,简单的说就是为某个对象提供一个代理,以控制对这个对象的访问。
代理分类?
-
静态代理:
-
代理类和被代理类实现同一个接口,且代理类中含有被代理类的引用,然后通过set方法或者构造器注入,既然实现同一个接口,肯定要实现同样的方法,在代理类的方法中通过被代理类的引用调用其同名方法,并在此调用前后增添自定义的方法,后续客户端直接调用代理类的某个方法,实际上是调用被代理类的同名方法,只不过这个方法在代理类中被增强了
-
代码示例:
// 公共接口 public interface UserService { void queryUserById(String id); } // 被代理类 public class UserServiceImpl implements UserService { @Override public void queryUserById(String id) { System.out.println("查询中,用户id为" + id); } } // 代理类1 public class UserServiceLoggerProxyImpl implements UserService { private UserService userService; public UserServiceLoggerProxyImpl(UserService userService) { this.userService = userService; } @Override public void queryUserById(String id) { System.out.println("记录日志,查询用户id为" + id); userService.queryUserById(id); } } // 代理类2 public class UserServiceTimerProxyImpl implements UserService { private UserService userService; public UserServiceTimerProxyImpl(UserService userService) { this.userService = userService; } @Override public void queryUserById(String id) { System.out.println("记录时间,查询用户id为" + id); userService.queryUserById(id); } } // 客户端 public class Client { public static void main(String[] args) { // 被代理类 UserService userService = new UserServiceImpl(); // 情况1,只记录日志 UserService userServiceLoggerProxyImpl = new UserServiceLoggerProxyImpl(userService); userServiceLoggerProxyImpl.queryUserById("123"); System.out.println("=============="); // 情况2,只记录时间 UserService userServiceTimerProxyImpl = new UserServiceTimerProxyImpl(userService); userServiceTimerProxyImpl.queryUserById("456"); System.out.println("=============="); // 情况3,记录日志和时间 UserService userServiceLoggerProxyImpl1 = new UserServiceLoggerProxyImpl(userService); UserService userServiceTimerProxyImpl1 = new UserServiceTimerProxyImpl(userServiceLoggerProxyImpl1); userServiceTimerProxyImpl1.queryUserById("789"); } } -
静态代理的优点:逻辑简单、明了,缺点:各种代理类是写死的,如果要对代理类中的增强方法进行改动,且还要保持原有的逻辑,则必须新写代理类,或者是如果被代理类一多,同样的代理类也要增多
-
-
动态代理:
- 在程序运行期间根据需要动态创建代理类及其实例来完成具体的功能。动态代理主要分为JDK动态代理和cglib动态代理两大类,本文主要对JDK动态代理进行探讨。
- ”动态生成代理类“,那么如何动态生成代理类呢?思路是怎样的?
-
不如先自己手动生成一个代理类吧:(这里手动生成上文静态代理类中的UserServiceLoggerProxyImpl)
-
手动生成一个.java文件,文件里的内容手动拼接
-
将.java文件写在磁盘上
-
利用编译工具,将磁盘的.java文件编译成.class文件
-
通过类加载器将.class文件加载至jvm中,会有代理类的Class字节码对象了
-
通过反射调用newInstance方法或者获取构造器调用newInstance方法获取代理类对象
package com.dyx.util; 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.InvocationTargetException; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; public class ProxyGeneratorUtil { // 手动生成代理类,并获取代理类实例,以UserServiceLoggerProxyImpl为例, // 需要一个参数,表示被代理的类的实例,使用target,类型是接口类型,比如UserService target,接口类型不定,所以使用Object public static Object newProxyInstance(Object target) { Object proxy = null; // 1. 拼接.java文件 String allContent = ""; // 1.1 包名,假如把生成的.java文件放在自定义的包下 String packageName = "mypackage"; String packageContent = "package " + packageName + ";\n\n"; // 1.2 import语句,需要导入接口所在的包,先获取其要实现的接口类型,假设被代理类只实现了一个接口 Class<?> targetInterface = target.getClass().getInterfaces()[0]; // 1.3 获取接口的名字 String targetInterfaceSimpleName = targetInterface.getSimpleName(); // 1.4 导包语句 String importContent = "import " + targetInterface.getName() + ";\n\n"; // 1.5 "public class 类名" 语句 String className = "CustomProxyImpl";// 类名 String classContent = "public class " + className + " implements " + targetInterfaceSimpleName + " {\n\n"; // 1.6 属性语句 String fieldContent = "\tprivate " + targetInterfaceSimpleName + " target;\n\n"; // 1.7 构造器语句 String constructorContent = "\tpublic " + className + "(" + targetInterfaceSimpleName + " target" + ") {\n" + "\t\tthis.target = target;\n\t}\n"; // 1.8 各方法语句,可能不止一个方法 StringBuffer methodContents = new StringBuffer(); Method[] declaredMethods = targetInterface.getDeclaredMethods(); for (Method method : declaredMethods) { String methodContent = ""; // 1.9 方法名字 String methodName = method.getName(); // 1.10 方法的返回类型 Class<?> methodReturnType = method.getReturnType(); // 1.11 方法的参数名字可自定义(使用arg+序号),需要获取参数的类型 String methodParameterContents = ""; String methodParameterNamePrefix = "arg"; int methodParameterNameSuffix = 0; String methodParameterName = "";// 形参中的参数 String methodParameterNameContent = ""; // 实参中的参数 Class<?>[] methodParameterTypes = method.getParameterTypes(); for (Class<?> methodParameterType : methodParameterTypes) { // 1.12 方法的参数类型 String methodParameterTypeSimpleName = methodParameterType.getSimpleName(); methodParameterName = methodParameterNamePrefix + methodParameterNameSuffix++; methodParameterTypeSimpleName += " " + methodParameterName + ", "; methodParameterContents += methodParameterTypeSimpleName; methodParameterNameContent += methodParameterName + ", "; } // 1.13 参数列表可能为空 if (methodParameterContents.length() > 0) { methodParameterContents = methodParameterContents.substring(0, methodParameterContents.lastIndexOf(",")); methodParameterNameContent = methodParameterNameContent.substring(0, methodParameterNameContent.lastIndexOf(",")); } // 1.14 自定义的增强方法,直接写死看看效果 String customEnhancedMethod = "System.out.println(\"这是自定义的增强方法...\");"; methodContent = "\n\tpublic " + methodReturnType + " " + methodName + "(" + methodParameterContents + ") {\n" + "\t\t" + customEnhancedMethod +"\n" + "\t\ttarget." + methodName + "(" + methodParameterNameContent + ");\n\t}\n"; methodContents.append(methodContent); } allContent += packageContent + importContent + classContent + fieldContent + constructorContent + methodContents + "}"; System.out.println("生成的Java文件内容=====>\n" + allContent); // 2 .java文件写到磁盘上,当前工程项目的路径下 File file = new File(""); FileWriter fileWriter = null; String path = ""; try { path = file.getCanonicalPath(); System.out.println("项目的绝对路径======>" + path); String dirName = path + File.separator + packageName + File.separator; file = new File(dirName); if (!file.exists()) { file.mkdirs(); } file = new File(dirName + className + ".java"); if (!file.exists()) { file.createNewFile(); } String filePath = file.getCanonicalPath(); System.out.println("Java文件的生成路径=====>" + filePath); fileWriter = new FileWriter(file); fileWriter.write(allContent); fileWriter.flush(); } catch (IOException e) { e.printStackTrace(); } finally { if (fileWriter != null) { try { fileWriter.close(); } catch (IOException e) { e.printStackTrace(); } } } // 3 编译 JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager fileManager = javaCompiler.getStandardFileManager(null,null,null); Iterable units = fileManager.getJavaFileObjects(file); JavaCompiler.CompilationTask task = javaCompiler.getTask(null, fileManager,null,null,null, units); task.call(); try { fileManager.close(); } catch (IOException e) { e.printStackTrace(); } // 4 类加载 try { String classFilePath = "file:" + path + File.separator; System.out.println("编译生成class字节码文件的路径,必须与包路径分开=====>" + classFilePath); URL[] urls = new URL[]{new URL(classFilePath)}; URLClassLoader urlClassLoader = new URLClassLoader(urls); Class<?> clazz = urlClassLoader.loadClass(packageName + "." + className); System.out.println("class字节码文件成功被加载=====>" + clazz); Constructor<?> constructor = clazz.getConstructor(targetInterface); proxy = constructor.newInstance(target); } catch (MalformedURLException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return proxy; } } -
测试类:
package com.dyx.util; import com.dyx.proxy.UserService; import com.dyx.proxy.UserServiceImpl; import org.junit.Test; public class TestProxyGenerator { @Test public void test() { // 被代理类 UserService userService = new UserServiceImpl(); UserService proxyInstance = (UserService) ProxyGeneratorUtil.newProxyInstance(userService); proxyInstance.queryUserById("111","1112111"); } }
-
-
JDK中本身的动态代理:
-
流程:
- 为接口创建代理类的字节码文件
- 使用ClassLoader将字节码文件加载到JVM
- 创建代理类实例对象,执行对象的目标方法
-
代码示例:
// 接口 package com.dyx.dynamic.proxy; public interface UserService { void queryUserById(String id,String id3); void sett(short if1, String ij); } // 被代理类 package com.dyx.dynamic.proxy; public class UserServiceImpl implements UserService { @Override public void queryUserById(String id,String id1) { System.out.println("查询中,用户id为" + id + ", 新用户为" + id1); } @Override public void sett(short if1, String ij) { } } // 动态代理类 package com.dyx.dynamic.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class UserServiceDynamicProxy<T> implements InvocationHandler { // 被代理类的目标实例 private T target; public T getTarget() { return target; } // 关键:生成动态代理类对实例,该实例可以看做是被代理类的实例(target), public T getProxy(T target) { this.target = target; return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this); } // @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { InvocationHandler invocationHandler = Proxy.getInvocationHandler(proxy); System.out.println("invocationHandler=====>" + invocationHandler); Class<?> proxyClass = Proxy.getProxyClass(target.getClass().getClassLoader(),target.getClass().getInterfaces()); System.out.println("proxyClass=====>" + proxyClass); System.out.println("target=====>" + target); // 此处可以进行动态代理类的增加的前置处理... System.out.println("前置处理..."); Object object = method.invoke(target, args); // 此处可以进行动态代理类的增加的后置处理... System.out.println("后置处理..."); System.out.println("object=====>" + object); return object; } } // 客户端 package com.dyx.dynamic.proxy; public class Client { public static void main(String[] args) { // 被代理类 UserService userService = new UserServiceImpl(); UserServiceDynamicProxy<UserService> userServiceDynamicProxy = new UserServiceDynamicProxy(); UserService proxy = userServiceDynamicProxy.getProxy(userService); proxy.queryUserById("1222","1212121"); // ProxyUtil.generateClassFile(UserService.class, proxy.getClass().getName()); } } -
动态代理涉及到的主要类:
java.lang.reflect.Proxy java.lang.reflect.InvocationHandler java.lang.reflect.WeakCache sun.misc.ProxyGenerator -
首先看Proxy类中的newProxyInstance方法:
@CallerSensitive public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { // 判断InvocationHandler是否为空,若为空,抛出空指针异常 Objects.requireNonNull(h); final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * 生成接口的代理类的字节码文件 */ Class<?> cl = getProxyClass0(loader, intfs); /* * 使用自定义的InvocationHandler作为参数,调用构造函数获取代理类对象实例 */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); } 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); } } -
newProxyInstance方法调用getProxyClass0方法生成代理类的字节码文件
private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { // 限定代理的接口不能超过65535个 if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } // 如果缓存中已经存在相应接口的代理类,直接返回;否则,使用ProxyClassFactory创建代理类 return proxyClassCache.get(loader, interfaces); } -
其中缓存使用的是WeakCache实现的,此处主要关注使用ProxyClassFactory创建代理的情况。ProxyClassFactory是Proxy类的静态内部类,实现了BiFunction接口,实现了BiFunction接口中的apply方法。当WeakCache中没有缓存相应接口的代理类,则会调用ProxyClassFactory类的apply方法来创建代理类。
private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>> { // 代理类前缀 private static final String proxyClassNamePrefix = "$Proxy"; // 生成代理类名称的计数器 private static final AtomicLong nextUniqueNumber = new AtomicLong(); @Override public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); for (Class<?> intf : interfaces) { /* * 校验类加载器是否能通过接口名称加载该类 */ Class<?> interfaceClass = null; try { interfaceClass = Class.forName(intf.getName(), false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != intf) { throw new IllegalArgumentException( intf + " is not visible from class loader"); } /* * 校验该类是否是接口类型 */ if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } /* * 校验接口是否重复 */ if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } } String proxyPkg = null; // 代理类包名 int accessFlags = Modifier.PUBLIC | Modifier.FINAL; /* * 非public接口,代理类的包名与接口的包名相同 */ for (Class<?> intf : interfaces) { int flags = intf.getModifiers(); 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) { // public代理接口,使用com.sun.proxy包名 proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } /* * 为代理类生成名字 */ long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num; /* * 真正生成代理类的字节码文件的地方 */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { // 使用类加载器将代理类的字节码文件加载到JVM中 return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { throw new IllegalArgumentException(e.toString()); } } } -
在ProxyClassFactory类的apply方法中可看出真正生成代理类字节码的地方是ProxyGenerator类中的generateProxyClass,该类未开源,但是可以使用IDEA、或者反编译工具jd-gui来查看。
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) { ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2); final byte[] var4 = var3.generateClassFile(); // 是否要将生成代理类的字节码文件保存到磁盘中 if (saveGeneratedFiles) { // .... } return var4; } -
在测试案例中,设置系统属性sun.misc.ProxyGenerator.saveGeneratedFiles值为true
// 保存生成的代理类的字节码文件 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");或者是构建一个工具类将生成的代理类的字节码文件保存
package com.dyx.dynamic.proxy; import sun.misc.ProxyGenerator; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; /** * 动态生成代理类测试 */ public class ProxyUtil { /** * 根据类信息,动态生成二进制文件并保存在硬盘中,默认的是clazz目录下, * @param clazz 需要生成动态代理类的类(接口) * @param proxyName 动态生成代理类的名称 */ public static void generateClassFile(Class clazz, String proxyName) { System.out.println("proxyName=====>" + proxyName); byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, new Class[]{clazz}); System.out.println("clazz=====>" + clazz); String path = clazz.getResource(".").getPath(); System.out.println("path=====>" + path); FileOutputStream fileOutputStream = null; try { fileOutputStream = new FileOutputStream(path + proxyName + ".class"); fileOutputStream.write(classFile); fileOutputStream.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } -
打开$Proxy0.class文件如下:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package com.sun.proxy; import com.dyx.dynamic.proxy.UserService; 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 UserService { private static Method m1; private static Method m4; 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 sett(short var1, String var2) throws { try { super.h.invoke(this, m4, new Object[]{var1, var2}); } catch (RuntimeException | Error var4) { throw var4; } catch (Throwable var5) { throw new UndeclaredThrowableException(var5); } } public final void queryUserById(String var1, String var2) throws { try { super.h.invoke(this, m3, new Object[]{var1, var2}); } catch (RuntimeException | Error var4) { throw var4; } catch (Throwable var5) { throw new UndeclaredThrowableException(var5); } } 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")); m4 = Class.forName("com.dyx.dynamic.proxy.UserService").getMethod("sett", Short.TYPE, Class.forName("java.lang.String")); m3 = Class.forName("com.dyx.dynamic.proxy.UserService").getMethod("queryUserById", Class.forName("java.lang.String"), Class.forName("java.lang.String")); 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()); } } } -
我们可以看到代理类内部实现比较简单,在调用每个代理类每个方法的时候,都用反射去调newProxyInstance方法中传来的h的invoke方法(也就是我们自定义的InvocationHandler的子类中重写的invoke方法),用参数传递了代理类实例、接口方法、调用参数列表,这样我们在重写的invoke方法中就可以实现对所有方法的统一包装了。
-