一、代理设计模式
代理设计模式是Java常用的设计模式之一。
特点:
01.委托类和代理类有共同的接口或者父类;
02.代理类负责为委托类处理消息,并将消息转发给委托类;
03.委托类和代理类对象通常存在关联关系,一个代理类对象与一个委托类对象关联;
04.代理类本身不是真正的实现者,而是通过调用委托类方法来实现代理功能;
二、静态代理与动态代理
按照代理类创建的时机,代理类分为两种:
01.静态代理:由我们程序猿或者特定的工具自动生成了源代码,在程序运行之前,class文件已经存在了;例如在serviceImpl.java中调用dao.xx(),真正的实现者是dao,service就可以理解为一个代理类;
02.动态代理:在程序运行期间,通过反射创建出来的代理类;
三、jdk动态代理
顾名思义,这种方式是由jdk为我们提供的。下面通过一个例子来演示。
01.创建一个Person接口
public interface Person { void eat(); void sleep(); }
02.创建ZhangSan.java实现Person接口
public class ZhangSan implements Person { public void eat() { System.out.println("吃饭..."); } public void sleep() { System.out.println("睡觉..."); } }
03.创建代理类ZSProxy.java
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class ZSProxy implements InvocationHandler { //被代理类对象引用 private ZhangSan zhangSan; public ZSProxy(ZhangSan zhangSan) { super(); this.zhangSan = zhangSan; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(); //zhangSan.eat(); Object result = method.invoke(zhangSan, args);//可以获取目标方法的返回值 after(); return null; } private void before() { System.out.println("前置..."); } private void after() { System.out.println("后置..."); } }
jdk动态代理中必须了解一个类和一个接口:Proxy类和InvocationHandler接口。
001.上述中的代理类实现了 InvocationHandler接口,此接口只有一个invoke方法
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
proxy:代理类对象
method:被代理的方法
args:被代理方法的参数列表
002.Proxy类
public class Proxy implements java.io.Serializable { ... public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException ... }
loader:类加载器
interfaces:代理类实现的所有接口
h:InvocationHandler接口的一个实例
public class Test { public static void main(String[] args) throws Throwable { System.out.println("----------------------JDK动态代理----------------------------"); //获取代理类对象 Person proxyInstance = (Person)Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[] {Person.class}, new ZSProxy(new ZhangSan())); proxyInstance.eat(); }
通过Proxy的newProxyInstance方法创建出代理对象,再有代理对象执行方法。
程序运行结果:
虽然效果实现了,但我们并不能从代码看到哪里调用的invoke方法??那么底层到底是怎么执行的呢??
首先要了解一个类 ==》$Proxy0.java
001. $Proxy0 是内存中的代理类,在$Proxy0中会持有一个InvocationHandler接口的实例类的引用,所以此Test类先是调用了内存中的$Proxy0.eat();
002.执行$Proxy0类中的invoke
我们debug运行观察:
内存中代理类的特征:
01.它是对目标类的代理,那么这个内存中中的代理类的类型必须和被代理类(目标类)的类型一致。
代码中 (Person)Proxy.newProxyInstance.. 进行了向下转型操作。
02.内存中的代理类必须持有InvocationHandler接口的实现类引用。
整个过程中$Proxy0我们是看不到的!那么有没有办法让它原形毕露呢?
/** * 使用IO的方式将内存中代理类写入到文件中,然后反编译出来进行观察 */ private static void createProxyClassFile() { byte[] data = ProxyGenerator.generateProxyClass("$Proxy0.class", new Class[] {Person.class}); try { FileOutputStream out = new FileOutputStream("$Proxy0.class"); out.write(data); out.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
对$Proxy0.java反编译后
public final class class extends Proxy implements Person { private static Method m1; private static Method m4; private static Method m2; private static Method m3; private static Method m0; public class(InvocationHandler invocationhandler) //通过构造将handler传入 { super(invocationhandler); } public final boolean equals(Object obj) { try { return ((Boolean)super.h.invoke(this, m1, new Object[] { obj })).booleanValue(); } catch (Error ) { } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final void eat() { try { super.h.invoke(this, m4, null);// h:就是上文说的 此类中必须存在InvocationHandler接口实现类的引用,也是这里真正调用了invoke方法 return; } catch (Error ) { } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final String toString() { try { return (String)super.h.invoke(this, m2, null); } catch (Error ) { } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final void sleep() { try { super.h.invoke(this, m3, null); return; } catch (Error ) { } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final int hashCode() { try { return ((Integer)super.h.invoke(this, m0, null)).intValue(); } catch (Error ) { } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m4 = Class.forName("cn.yzx.jdkProxy.Person").getMethod("eat", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m3 = Class.forName("cn.yzx.jdkProxy.Person").getMethod("sleep", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); } catch (NoSuchMethodException nosuchmethodexception) { throw new NoSuchMethodError(nosuchmethodexception.getMessage()); } catch (ClassNotFoundException classnotfoundexception) { throw new NoClassDefFoundError(classnotfoundexception.getMessage()); } } }
那么就真相大白了。
********************************************************************************************************************************************
以上就是jdk为我们提供的动态代理。我们也可以模仿它的实现原理,自定义我们的动态代理。
01.创建Person接口
package cn.yzx.myProxy; public interface Person { void eat() throws Throwable; void sleep() throws Throwable; }
02.创建ZhangSan实现类
package cn.yzx.myProxy; public class ZhangSan implements Person { public void eat() { System.out.println("吃饭..."); } public void sleep() { System.out.println("睡觉..."); } }
03.ZSProxy代理类
package cn.yzx.myProxy;
import java.lang.reflect.Method; public class ZSProxy implements MyInvocationHandler { //目标对象 private Person person; public ZSProxy(Person person) { this.person = person; } @Override public Object invoke(Object proxy, Method method, Object args) { before(); try { person.eat(); } catch (Throwable e) { e.printStackTrace(); } after(); return null; } private void before() { System.out.println("前置..."); } private void after() { System.out.println("后置..."); } }
04.自定义动态代理Proxy类
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 javax.tools.JavaCompiler; import javax.tools.JavaCompiler.CompilationTask; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; /** * 自定义的动态代理Proxy类 * @author Xuas */ public class MyProxy { static String rt = "\r\n"; /** * 自定义创建内存中代理实例的方法 * @param loader * @param interfaces * @param handler * @return */ public static Object newProxyInstance(ClassLoader loader, Class interfaces, MyInvocationHandler handler) { try { Method[] methods = interfaces.getMethods(); //01.使用拼凑字符串的方式将内存中的代理类拼出来 String proxyClass = "package cn.yzx.myProxy;" + rt + "import java.lang.reflect.Method;" + rt + "public class $Proxy0 implements " + interfaces.getName() + "{" + rt + "MyInvocationHandler h;" + rt + "public $Proxy0(MyInvocationHandler h) {" + rt + "this.h = h;" + rt + "}" + getMethodString(methods, interfaces) + rt + "}"; //02.使用IO将拼凑的代理类写入到文件中 String filePathName = "F:/Java/SpringAopDemo/src/main/java/cn/yzx/myProxy/$Proxy0.java"; File file = new File(filePathName); FileWriter fw = new FileWriter(file); fw.write(proxyClass); fw.flush(); fw.close(); //03.编译上面生成的java文件 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);//文件管理器 Iterable javaFileObjects = fileManager.getJavaFileObjects(filePathName);//获取java文件 CompilationTask task = compiler.getTask(null, fileManager, null, null, null, javaFileObjects);//获取编译任务 task.call();//编译 fileManager.close();//关闭文件管理器 /** * 此时被编译后的class文件还在硬盘之中!它应该存在于JVM内存中! * 所以要把硬盘中class文件加载到JVM内存中去! */ //04.把硬盘中的class文件加载到内存中去,要使用ClassLoader MyClassLoader loader1 = new MyClassLoader("F:/Java/SpringAopDemo/src/main/java/cn/yzx/myProxy"); //proxy0Clazz:就是内存中代理类的class对象 Class<?> proxy0Clazz = loader1.findClass("$Proxy0");//返回被代理类的class对象 /** * public $Proxy0(MyInvocationHandler h) {..} * 要通过构造器的参数将handler对象的引用传进来 */ Constructor<?> constructor = proxy0Clazz.getConstructor(MyInvocationHandler.class); Object instance = constructor.newInstance(handler);//返回内存中代理类实例 return instance; } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return null; } private static String getMethodString(Method[] methods, Class interfaces) { String proxyMe = ""; for (Method method : methods) { proxyMe += "public void " + method.getName() + "() throws Throwable {" + rt + "Method md = " + interfaces.getName() + ".class.getMethod(\"" + method.getName() + "\",new Class[]{});" + rt + "this.h.invoke(this,md,null);" + rt + "}" + rt; } return proxyMe; } }
05.自定义InvocationHandler接口
import java.lang.reflect.Method; /** * 自定义InvocationHandler接口 * @author Xuas * */ public interface MyInvocationHandler { public Object invoke(Object proxy, Method method, Object args); }
06.自定义的类加载器
package cn.yzx.myProxy; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; public class MyClassLoader extends ClassLoader { private File dir; public MyClassLoader(String path) {//要加载的文件路径 dir = new File(path); } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { if(null != dir) { File clazzFile = new File(dir, name + ".class"); if(clazzFile.exists()) { try { FileInputStream input = new FileInputStream(clazzFile); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; while((len = input.read(buffer)) != -1) { baos.write(buffer, 0, len); } return defineClass("cn.yzx.myProxy."+name, baos.toByteArray(), 0, baos.size());//最终把输出流输出到JVM内存中 } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } return super.findClass(name); } }
07.内存中自定义生成的 $Proxy0.java
package cn.yzx.myProxy; import java.lang.reflect.Method; public class $Proxy0 implements cn.yzx.myProxy.Person{ MyInvocationHandler h; public $Proxy0(MyInvocationHandler h) { this.h = h; } public void sleep() throws Throwable { Method md = cn.yzx.myProxy.Person.class.getMethod("sleep",new Class[]{}); this.h.invoke(this,md,null); } public void eat() throws Throwable { Method md = cn.yzx.myProxy.Person.class.getMethod("eat",new Class[]{}); this.h.invoke(this,md,null); } }
08.测试类
public class Test { public static void main(String[] args) throws Throwable { System.out.println("----------------------自定义动态代理----------------------------"); cn.yzx.myProxy.Person person2 = (cn.yzx.myProxy.Person)MyProxy.newProxyInstance(cn.yzx.myProxy.Person.class.getClassLoader(), cn.yzx.myProxy.Person.class, new cn.yzx.myProxy.ZSProxy(new cn.yzx.myProxy.ZhangSan())); person2.eat(); } }
程序运行结果:
总结:
通过观察Proxy中的newProxyInstance方法的参数可知,jdk动态代理只支持委托类和代理类实现共同的接口的方式。
出处:https://www.cnblogs.com/9513-/p/8432276.html