一:自己手写动态代理
1.1,把一串静态的字符串(代理类)加载输出,编译,加载到内存,并且反射获取这个对象
public interface Moveable { public void move(); }
1 package com.DesignPatterns.al.Dynamic5; 2 3 import java.util.Random; 4 5 public class Tank implements Moveable { 6 7 @Override 8 public void move() { 9 10 System.out.println("坦克在移动"); 11 try { 12 Thread.sleep(new Random().nextInt(1000)); 13 } catch (InterruptedException e) { 14 e.printStackTrace(); 15 } 16 17 } 18 }
package com.DesignPatterns.al.Dynamic5; import java.io.File; import java.io.FileWriter; import java.lang.reflect.Constructor; import java.net.URL; import java.net.URLClassLoader; import javax.tools.JavaCompiler; import javax.tools.JavaCompiler.CompilationTask; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; public class Test { public static void main(String[] args) throws Exception { String rt="\r\n"; String src= "package com.DesignPatterns.al.Dynamic5;"+rt+ "public class TankTime implements Moveable {"+rt+ " Moveable t;"+rt+ " public TankTime(Moveable t) {"+rt+ " super();"+rt+ " this.t = t;"+rt+ " }"+rt+ " @Override"+rt+ " public void move() {"+rt+ " long startTime = System.currentTimeMillis();"+rt+ " System.out.println(\"tank开始打印开始时间\"+startTime);"+rt+ " t.move();"+rt+ " long endTime = System.currentTimeMillis();"+rt+ " System.out.println(\"tank总共花费时间是\" + (endTime - startTime));"+rt+ " }"+rt+ "}"; //1,生成代理类 String fileName = System.getProperty("user.dir"); System.out.println("fileName是项目的根路径:"+fileName); String fileNameEnd=fileName+"/src/com/DesignPatterns/al/Dynamic5/TankTime.java"; File f=new File(fileNameEnd); FileWriter fw=new FileWriter(f); fw.write(src); fw.flush(); fw.close(); //2,开始生成class文件,进行编译 //2.1,获取java默认的编译器,说白了就是javac JavaCompiler compiler=ToolProvider.getSystemJavaCompiler(); System.out.println("现在我的编译器是"+compiler.getClass().getName()); StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null); Iterable units = fileMgr.getJavaFileObjects(fileNameEnd); CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units); //2.2,生成class文件这里不是重点,我们知道通过上面的代码就可以把java代码生成到指定的目录下面的class文件 t.call(); fileMgr.close(); //3,把class文件加载到内存中去 URL[] urls = new URL[] {new URL("file:/" + System.getProperty("user.dir") +"/src")}; URLClassLoader ul = new URLClassLoader(urls); Class c = ul.loadClass("com.DesignPatterns.al.Dynamic5.TankTime"); System.out.println("加载我的class文件是"+c); //4,从内存中生成对象,因为这个时候我们没有这个java类,所以只有从内存中来区这个值。 //换句话就是说,我们现在没有这个对象,只有通过反射(通过构造器反射来获取这个对象) //4.1,我们调用的是构造方法的参数为Moveable(上面的参数是它的实现也可以的)类型的构造方法 Constructor ctr = c.getConstructor(Moveable.class); Moveable m = (Moveable)ctr.newInstance(new Tank()); m.move(); } }
通过运行,我们得到动态获取下面的代码:
package com.DesignPatterns.al.Dynamic5; public class TankTime implements Moveable { Moveable t; public TankTime(Moveable t) { super(); this.t = t; } @Override public void move() { long startTime = System.currentTimeMillis(); System.out.println("tank开始打印开始时间"+startTime); t.move(); long endTime = System.currentTimeMillis(); System.out.println("tank总共花费时间是" + (endTime - startTime)); } }
控制台打印出下面的
fileName是项目的根路径:D:\data\eclipse-workspace\DesignPatterns 现在我的编译器是com.sun.tools.javac.api.JavacTool 加载我的class文件是class com.DesignPatterns.al.Dynamic5.TankTime tank开始打印开始时间1541264914598 坦克在移动 tank总共花费时间是531
1.2,升级------ 静态的字符串(代理类)------里面的接口变成动态的
package com.DesignPatterns.al.Dynamic6; import java.io.File; import java.io.FileWriter; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.net.URLClassLoader; import javax.tools.JavaCompiler; import javax.tools.JavaCompiler.CompilationTask; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; public class Proxy { public static Object newProxyInstance(Class inferance) throws Exception { String rt="\r\n"; String src= "package com.DesignPatterns.al.Dynamic6;"+rt+ "public class TankTime implements "+inferance.getName()+ "{"+rt+ " Moveable t;"+rt+ " public TankTime(Moveable t) {"+rt+ " super();"+rt+ " this.t = t;"+rt+ " }"+rt+ " @Override"+rt+ " public void move() {"+rt+ " long startTime = System.currentTimeMillis();"+rt+ " System.out.println(\"tank开始打印开始时间\"+startTime);"+rt+ " t.move();"+rt+ " long endTime = System.currentTimeMillis();"+rt+ " System.out.println(\"tank总共花费时间是\" + (endTime - startTime));"+rt+ " }"+rt+ "}"; //1,生成代理类 String fileName = System.getProperty("user.dir"); System.out.println("fileName是项目的根路径:"+fileName); String fileNameEnd=fileName+"/src/com/DesignPatterns/al/Dynamic6/TankTime.java"; File f=new File(fileNameEnd); FileWriter fw=new FileWriter(f); fw.write(src); fw.flush(); fw.close(); //2,开始生成class文件,进行编译 //2.1,获取java默认的编译器,说白了就是javac JavaCompiler compiler=ToolProvider.getSystemJavaCompiler(); System.out.println("现在我的编译器是"+compiler.getClass().getName()); StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null); Iterable units = fileMgr.getJavaFileObjects(fileNameEnd); CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units); //2.2,生成class文件这里不是重点,我们知道通过上面的代码就可以把java代码生成到指定的目录下面的class文件 t.call(); fileMgr.close(); //3,把class文件加载到内存中去 URL[] urls = new URL[] {new URL("file:/" + System.getProperty("user.dir") +"/src")}; URLClassLoader ul = new URLClassLoader(urls); Class c = ul.loadClass("com.DesignPatterns.al.Dynamic6.TankTime"); System.out.println("加载我的class文件是"+c); //4,从内存中生成对象 //4.1,我们调用的是构造方法的参数为Moveable(上面的参数是它的实现也可以的)类型的构造方法 Constructor ctr = c.getConstructor(Moveable.class); Moveable m = (Moveable)ctr.newInstance(new Tank()); m.move(); return null; } }
1.3,升级------ 静态的字符串(代理类)------里面的接口变成动态的----里面的方法变成动态的
public interface Moveable { public void move(); }
public class Test { public static void main(String[] args) { Method[] methods = Moveable.class.getMethods(); for(Method m : methods) { System.out.println(m.getName()); } } }
上面的代码是通过发射动态的来获取方法的实例。看下面真正的改造
package com.DesignPatterns.al.Dynamic8; public interface Moveable { public void move(); }
package com.DesignPatterns.al.Dynamic8; import java.util.Random; public class Tank implements Moveable { @Override public void move() { System.out.println("坦克在移动"); try { Thread.sleep(new Random().nextInt(1000)); } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.DesignPatterns.al.Dynamic8; 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; import javax.tools.JavaCompiler; import javax.tools.JavaCompiler.CompilationTask; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; public class Proxy { public static Object newProxyInstance(Class inferance) throws Exception { String methodStr = ""; Method[] methods = inferance.getMethods(); String rt="\r\n"; for(Method m : methods) { methodStr += "@Override" + rt + "public void " + m.getName() + "() {" + rt + " long start = System.currentTimeMillis();" + rt + " System.out.println(\"starttime:\" + start);" + rt + " t." + m.getName() + "();" + rt + " long end = System.currentTimeMillis();" + rt + " System.out.println(\"time:\" + (end-start));" + rt + "}"; } String src= "package com.DesignPatterns.al.Dynamic8;"+rt+ "public class TankTime implements "+inferance.getName()+ "{"+rt+ " Moveable t;"+rt+ " public TankTime(Moveable t) {"+rt+ " super();"+rt+ " this.t = t;"+rt+ " }"+rt+ methodStr +rt+ "}"; //1,生成代理类 String fileName = System.getProperty("user.dir"); System.out.println("fileName是项目的根路径:"+fileName); String fileNameEnd=fileName+"/src/com/DesignPatterns/al/Dynamic8/TankTime.java"; File f=new File(fileNameEnd); FileWriter fw=new FileWriter(f); fw.write(src); fw.flush(); fw.close(); //2,开始生成class文件,进行编译 //2.1,获取java默认的编译器,说白了就是javac JavaCompiler compiler=ToolProvider.getSystemJavaCompiler(); System.out.println("现在我的编译器是"+compiler.getClass().getName()); StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null); Iterable units = fileMgr.getJavaFileObjects(fileNameEnd); CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units); //2.2,生成class文件这里不是重点,我们知道通过上面的代码就可以把java代码生成到指定的目录下面的class文件 t.call(); fileMgr.close(); //3,把class文件加载到内存中去 URL[] urls = new URL[] {new URL("file:/" + System.getProperty("user.dir") +"/src")}; URLClassLoader ul = new URLClassLoader(urls); Class c = ul.loadClass("com.DesignPatterns.al.Dynamic8.TankTime"); System.out.println("加载我的class文件是"+c); //4,从内存中生成对象 //4.1,我们调用的是构造方法的参数为Moveable(上面的参数是它的实现也可以的)类型的构造方法 Constructor ctr = c.getConstructor(Moveable.class); Object o = ctr.newInstance(new Tank()); return o; } }
package com.DesignPatterns.al.Dynamic8; /** * * @author qingruihappy * @data 2018年11月1日 上午12:39:35 * @说明:现在模拟动态的方法并且打印主方法的内容 */ public class Test { public static void main(String[] args) throws Exception { Moveable newProxyInstance = (Moveable) Proxy.newProxyInstance(Moveable.class); // 是不是很神奇啊,现在假如我们现在没有看到代理类,我们只有接口类,tank类,还有测试类, // 我们直观的来看现在的方法就是,我们把接口传入进入了newProxy这个方法中,它居然动态的给我 // 打印出了日志还有主方法。 // 我们不看代理类里面的代码我们闭着眼来想一下它是怎么实现的。 // 1,首先把不用变的代码以静态string的形式写入进了动态类中 // 2,把动态变化的东西比如说接口,通过传入的接口来获取, // 3,方法,因为接口中就有了实现类的方法,所以通过接口也能获取,其中的方法, // 4,那它是怎么来确定实现类就是tank呢而不是其他的呢。往下面看 // 5,写完了之后生成java文件 // 6,编译生成class文件 // 7,加载到内存中来 // 8,从内从中反射生成对象。(注意这这里传入了tank确定了是tank对象,所以当下面调用) // 9,执行下面的方法,就相当于执行刚生成的tanktime里面的方法是一样的。 newProxyInstance.move(); // 我们来看看还有什么问题就是动态切入进来的代码不是随机变化的,下面就是让切入进来的代码变成随机变化的。 } }
上面的代码运行之后动态生成的java代理类是:
package com.DesignPatterns.al.Dynamic8; public class TankTime implements com.DesignPatterns.al.Dynamic8.Moveable{ Moveable t; public TankTime(Moveable t) { super(); this.t = t; } @Override public void move() { long start = System.currentTimeMillis(); System.out.println("starttime:" + start); t.move(); long end = System.currentTimeMillis(); System.out.println("time:" + (end-start)); } }
控制台打印出来的是:
fileName是项目的根路径:D:\data\eclipse-workspace\DesignPatterns 现在我的编译器是com.sun.tools.javac.api.JavacTool 加载我的class文件是class com.DesignPatterns.al.Dynamic8.TankTime starttime:1541265534048 坦克在移动 time:877
1.4,升级------ 静态的字符串(代理类)------里面的接口变成动态的----里面的方法变成动态的-----非业务代码的抽取(关键)
package com.DesignPatterns.al.Dynamic9; import java.lang.reflect.Method; public interface InvocationHandler { public void invoke(Object o, Method m); }
package com.DesignPatterns.al.Dynamic9; import java.lang.reflect.Method; public class TimeHandler implements InvocationHandler{ //这个target在test类中传参的时候就已经传入进来了 private Object target; public TimeHandler(Object target) { super(); this.target = target; } @Override public void invoke(Object o, Method m) { //Object o这里的o是代理对象的实例,这里我们没用,并不代表以后我们不用 System.out.println("代理类是谁呢?"+o.getClass().getName()); long start = System.currentTimeMillis(); System.out.println("starttime:" + start); try { //target是目标类不是代理类。目标类的方法,而不是代理类的方法。这里invoke是反射特有的方法,就是类(target).方法(m) m.invoke(target); } catch (Exception e) { e.printStackTrace(); } long end = System.currentTimeMillis(); System.out.println("time:" + (end-start)); } }
package com.DesignPatterns.al.Dynamic9; public interface Moveable { public void move(); }
package com.DesignPatterns.al.Dynamic9; import java.util.Random; public class Tank implements Moveable { @Override public void move() { System.out.println("坦克在移动"); try { Thread.sleep(new Random().nextInt(1000)); } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.DesignPatterns.al.Dynamic9; 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; import javax.tools.JavaCompiler; import javax.tools.JavaCompiler.CompilationTask; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; /** * jdk中的类名是$Proxy1,可以把下面的TankTime替换掉,其实就是一个名字意义不大,都一样 * * @author qingruihappy * @data 2018年11月2日 上午1:24:30 * @说明:其实可以这样理解对inferance接口里面的方法实行的是什么样(InvocationHandler)日志,时间,事物。。。等等的代理 */ public class Proxy { public static Object newProxyInstance(Class inferance, InvocationHandler h) throws Exception { String methodStr = ""; Method[] methods = inferance.getMethods(); String rt = "\r\n"; for (Method m : methods) { methodStr += "@Override" + rt + "public void " + m.getName() + "() {" + rt + " try {" + rt + " Method md = " + inferance.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt + " h.invoke(this, md);" + rt + " }catch(Exception e) {e.printStackTrace();}" + rt + "}"; } String src = "package com.DesignPatterns.al.Dynamic9;" + rt + "import java.lang.reflect.Method;" + rt + "public class TankTime implements " + inferance.getName() + "{" + rt + " public TankTime(InvocationHandler h) {" + rt + " super();" + rt + " this.h = h;" + rt + " }" + rt + " com.DesignPatterns.al.Dynamic9.InvocationHandler h;" + rt + methodStr + rt + "}"; // 1,生成代理类 String fileName = System.getProperty("user.dir"); System.out.println("fileName是项目的根路径:" + fileName); String fileNameEnd = fileName + "/src/com/DesignPatterns/al/Dynamic9/TankTime.java"; File f = new File(fileNameEnd); FileWriter fw = new FileWriter(f); fw.write(src); fw.flush(); fw.close(); // 2,开始生成class文件,进行编译 // 2.1,获取java默认的编译器,说白了就是javac JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); System.out.println("现在我的编译器是" + compiler.getClass().getName()); StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null); Iterable units = fileMgr.getJavaFileObjects(fileNameEnd); CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units); // 2.2,生成class文件这里不是重点,我们知道通过上面的代码就可以把java代码生成到指定的目录下面的class文件 t.call(); fileMgr.close(); // 3,把class文件加载到内存中去 URL[] urls = new URL[] { new URL("file:/" + System.getProperty("user.dir") + "/src") }; URLClassLoader ul = new URLClassLoader(urls); Class c = ul.loadClass("com.DesignPatterns.al.Dynamic9.TankTime"); System.out.println("加载我的class文件是" + c); // 4,从内存中生成对象 // 4.1,我们调用的是构造方法的参数为Moveable(上面的参数是它的实现也可以的)类型的构造方法 // 其实在这里我们可以更好的理解反射,传统意义上的代码我们都是现有java实体类,让后我们在去new的方法创造内存对象 // 但是假如现在我们有了使我们动态的java文件,并且已经生成class文件加载到内存中了 // 但是由于java文件不是我们自己写的,所以我们现在我们看不到java文件,所以就没有办法new对象了 // 但是我们在内存中已经有了这个对象了,现在我们要用这个对象,这个时候就需要用发射了 // 下面的代码就是实现了InvocationHandler接口的构造器反射获取构造器,然后再通过newInstance获取对象,h是接口具体的实现类 Constructor ctr = c.getConstructor(InvocationHandler.class); Object o = ctr.newInstance(h); return o; } }
package com.DesignPatterns.al.Dynamic9; /** * * @author qingruihappy * @data 2018年11月1日 上午12:39:35 * @说明:现在模拟配置类随意切换的问题 * * 现在可以对任意的对象,任意的接口方法,实现任意的代理 *现在我们写的差不多了,我们来捋一下思路 *1,我们首先要知道动态代理要干啥呢? *动态代理就是类似于面向切面编程,我们在不知不觉中间就把类似于日志,事物,权限,加解密等非业务代码逻辑加到了目标函数的前后。 *2,我们来看一下,我们平常遇到的jdk动态代理是怎么用的。 *2.1,写出目标(业务)函数,并且必须让它实现一个接口, *2.2,写出非业务代码的接口,并且让子类来实现非业务代码 *2.3,在非业务代码中又通过发射来调用业务代码,(注意这里面无非就是目标类.目标方法,反射特有的invoke方法) *2.4,通过(Proxy)动态的获取变化的代理类 *3,动态的获取变化的代理才是动态代理的难点,因为,生成代理类的proxy是封装过得,而生成的代理的java代码我们也是看不到的 *这个也就是难点 *3.1,至于如何获取请看下面 * // 1,首先把不用变的代码以静态string的形式写入进了动态类中 // 2,接口:把动态变化的东西比如说接口,通过传入的接口来获取, // 3,方法:因为接口中就有了实现类的方法,所以通过接口也能获取,其中的方法, // 4,回调:把非业务代码的实现了传入到Proxy中来,当调用第三步已经确定了的方法额时候,就明确的调用在非业务代码中的invoke方法 //4.1,目标:在非业务代码的中间反射调用业务代码 // 5,写出:写完了之后生成java文件 // 6,编译:编译生成class文件 // 7,内存:加载到内存中来 // 8,反射:从内从中反射生成对象。(注意这这里传入了tank确定了是tank对象,所以当下面调用) //9,对象:获取代理对象 */ public class Test { public static void main(String[] args) throws Exception { Tank t = new Tank(); InvocationHandler h = new TimeHandler(t); Moveable m = (Moveable) Proxy.newProxyInstance(Moveable.class, h); m.move(); } }
生成的代理类是:
package com.DesignPatterns.al.Dynamic9; import java.lang.reflect.Method; public class TankTime implements com.DesignPatterns.al.Dynamic9.Moveable{ public TankTime(InvocationHandler h) { super(); this.h = h; } com.DesignPatterns.al.Dynamic9.InvocationHandler h; @Override public void move() { try { Method md = com.DesignPatterns.al.Dynamic9.Moveable.class.getMethod("move"); h.invoke(this, md); }catch(Exception e) {e.printStackTrace();} } }
控制台
fileName是项目的根路径:D:\data\eclipse-workspace\DesignPatterns 现在我的编译器是com.sun.tools.javac.api.JavacTool 加载我的class文件是class com.DesignPatterns.al.Dynamic9.TankTime 代理类是谁呢?com.DesignPatterns.al.Dynamic9.TankTime starttime:1541265881417 坦克在移动 time:698
1.5,升级------ 静态的字符串(代理类)------里面的接口变成动态的----里面的方法变成动态的-----非业务代码的抽取----用我们自己写的动态代理测试在代码前后加事物
package com.DesignPatterns.al.Dynamic91; import java.lang.reflect.Method; public interface InvocationHandler { public void invoke(Object o, Method m); }
package com.DesignPatterns.al.Dynamic91; import java.lang.reflect.Method; public class TransactionHandler implements InvocationHandler { private Object target; public TransactionHandler(Object target) { super(); this.target = target; } @Override public void invoke(Object o, Method m) { System.out.println("Transaction Start"); try { m.invoke(target); } catch (Exception e) { e.printStackTrace(); } System.out.println("Transaction Commit"); } }
package com.DesignPatterns.al.Dynamic91; public interface UserMgr { void addUser(); }
package com.DesignPatterns.al.Dynamic91; public class UserMgrImpl implements UserMgr { @Override public void addUser() { System.out.println("1: 插入记录到user表"); System.out.println("2: 做日志在另外一张表"); } }
package com.DesignPatterns.al.Dynamic91; 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; import javax.tools.JavaCompiler; import javax.tools.JavaCompiler.CompilationTask; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; /** * jdk中的类名是$Proxy1,可以把下面的TankTime替换掉,其实就是一个名字意义不大,都一样 * @author qingruihappy * @data 2018年11月2日 上午1:24:30 * @说明: */ public class Proxy { public static Object newProxyInstance(Class inferance,InvocationHandler h) throws Exception { String methodStr = ""; Method[] methods = inferance.getMethods(); String rt="\r\n"; for(Method m : methods) { methodStr += "@Override" + rt + "public void " + m.getName() + "() {" + rt + " try {" + rt + " Method md = " + inferance.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt + " h.invoke(this, md);" + rt + " }catch(Exception e) {e.printStackTrace();}" + rt + "}"; } String src= "package com.DesignPatterns.al.Dynamic91;"+rt+ "import java.lang.reflect.Method;" + rt + "public class TankTime implements "+inferance.getName()+ "{"+rt+ " public TankTime(InvocationHandler h) {"+rt+ " super();"+rt+ " this.h = h;"+rt+ " }"+rt+ " com.DesignPatterns.al.Dynamic91.InvocationHandler h;" + rt + methodStr +rt+ "}"; //1,生成代理类 String fileName = System.getProperty("user.dir"); System.out.println("fileName是项目的根路径:"+fileName); String fileNameEnd=fileName+"/src/com/DesignPatterns/al/Dynamic91/TankTime.java"; File f=new File(fileNameEnd); FileWriter fw=new FileWriter(f); fw.write(src); fw.flush(); fw.close(); //2,开始生成class文件,进行编译 //2.1,获取java默认的编译器,说白了就是javac JavaCompiler compiler=ToolProvider.getSystemJavaCompiler(); System.out.println("现在我的编译器是"+compiler.getClass().getName()); StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null); Iterable units = fileMgr.getJavaFileObjects(fileNameEnd); CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units); //2.2,生成class文件这里不是重点,我们知道通过上面的代码就可以把java代码生成到指定的目录下面的class文件 t.call(); fileMgr.close(); //3,把class文件加载到内存中去 URL[] urls = new URL[] {new URL("file:/" + System.getProperty("user.dir") +"/src")}; URLClassLoader ul = new URLClassLoader(urls); Class c = ul.loadClass("com.DesignPatterns.al.Dynamic91.TankTime"); System.out.println("加载我的class文件是"+c); //4,从内存中生成对象 //4.1,我们调用的是构造方法的参数为Moveable(上面的参数是它的实现也可以的)类型的构造方法 Constructor ctr = c.getConstructor(InvocationHandler.class); Object o = ctr.newInstance(h); return o; } }
package com.DesignPatterns.al.Dynamic91; public class Client { public static void main(String[] args) throws Exception { UserMgr mgr = new UserMgrImpl(); InvocationHandler h = new TransactionHandler(mgr); UserMgr u = (UserMgr)Proxy.newProxyInstance(UserMgr.class,h); u.addUser(); } }
动态生成的代理类是:
package com.DesignPatterns.al.Dynamic91; import java.lang.reflect.Method; public class TankTime implements com.DesignPatterns.al.Dynamic91.UserMgr{ public TankTime(InvocationHandler h) { super(); this.h = h; } com.DesignPatterns.al.Dynamic91.InvocationHandler h; @Override public void addUser() { try { Method md = com.DesignPatterns.al.Dynamic91.UserMgr.class.getMethod("addUser"); h.invoke(this, md); }catch(Exception e) {e.printStackTrace();} } }
控制台:
fileName是项目的根路径:D:\data\eclipse-workspace\DesignPatterns 现在我的编译器是com.sun.tools.javac.api.JavacTool 加载我的class文件是class com.DesignPatterns.al.Dynamic91.TankTime Transaction Start 1: 插入记录到user表 2: 做日志在另外一张表 Transaction Commit
二,我们来看jdk自带的动态代理
案例一:
package com.DesignPatterns.al.Dynamic92; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * @Author: feiweiwei * @Description: * @Created Date: 09:50 17/9/22. * @Modify by: */ public class DynamicProxyAOP implements InvocationHandler { private Object subject; public DynamicProxyAOP(Object subject) { this.subject = subject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //在执行真实subject执行的方法 System.out.println("before do something"); //执行真实subject方法 //方法,对象,参数都有了就可以执行了。 Object rtn = method.invoke(subject, args); //在执行结束后再执行的方法 System.out.println("after do something"); return rtn; } }
package com.DesignPatterns.al.Dynamic92; /** * @Author: feiweiwei * @Description: * @Created Date: 09:47 17/9/22. * @Modify by: */ public interface Subject { public String doSomething(String name); }
package com.DesignPatterns.al.Dynamic92; /** * @Author: feiweiwei * @Description: * @Created Date: 09:49 17/9/22. * @Modify by: */ public class RealSubject implements Subject { @Override public String doSomething(String name) { System.out.println(name + " do something!"); return name + " do something!"; } }
package com.DesignPatterns.al.Dynamic92; /** * @Author: feiweiwei * @Description: * @Created Date: 09:56 17/9/22. * @Modify by: */ public class RealSubject2 implements Subject { @Override public String doSomething(String name) { System.out.println(name + " do something2!"); return name + "do something2!"; } }
package com.DesignPatterns.al.Dynamic92; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import sun.misc.ProxyGenerator; /** * @Author: feiweiwei * @Description: * @Created Date: 10:00 17/9/22. * @Modify by: */ public class DynamicProxyMain { public static void main(String[] args) throws IOException { // 目标类1 Subject realSubject = new RealSubject(); // 处理器类 InvocationHandler handler = new DynamicProxyAOP(realSubject); Subject subject = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject .getClass().getInterfaces(), handler); subject.doSomething("tester "); /* * // 目标类2 Subject realSubject2 = new RealSubject2(); */ // 多态指向DynamicProxyAOP的接口类InvocationHandler // 多态指向DynamicProxyAOP的接口类InvocationHandler /* InvocationHandler handler2 = new DynamicProxyAOP(realSubject2); */ /* * Subject subject2 = (Subject) Proxy.newProxyInstance(realSubject2.getClass().getClassLoader(), realSubject2 * .getClass().getInterfaces(), handler2); */ // subject2.doSomething("tester "); String path = "E:/$Proxy0.class"; byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", RealSubject.class.getInterfaces()); FileOutputStream out = null; try { out = new FileOutputStream(path); out.write(classFile); out.flush(); } catch (Exception e) { e.printStackTrace(); } finally { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } }
生成的代理类
import com.DesignPatterns.al.Dynamic92.Subject; 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 Subject { private static Method m1; private static Method m3; private static Method m0; private static Method m2; public $Proxy0(InvocationHandler paramInvocationHandler) throws { super(paramInvocationHandler); } public final boolean equals(Object paramObject) throws { try { return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final String doSomething(String paramString) throws { try { return (String)this.h.invoke(this, m3, new Object[] { paramString }); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final int hashCode() throws { try { return ((Integer)this.h.invoke(this, m0, null)).intValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final String toString() throws { try { return (String)this.h.invoke(this, m2, null); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m3 = Class.forName("com.DesignPatterns.al.Dynamic92.Subject").getMethod("doSomething", new Class[] { Class.forName("java.lang.String") }); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); return; } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); } } }
控制台:
before do something tester do something! after do something
是不是很像啊。
有兴趣的可以研究一下它的源码
下面是源码的链接文档:
https://blog.csdn.net/sum__mer/article/details/53179662
https://www.jianshu.com/p/269afd0a52e6
https://www.jianshu.com/p/471c80a7e831
kk
原文地址:https://www.cnblogs.com/qingruihappy/p/9902959.html
时间: 2024-11-09 02:39:29