jdk动态代理的使用
1.创建实现InvocationHandler接口的类,实现invoke(Object proxy, Method method, Object[] args)接口,其中invoke()执行的方法就为代理实例对象执行的方法。
其中proxy为代理对象,method为方法,args为方法的参数。
要想在原类方法上在进行再处理(如记录日志等),需要用构造方法把接口子类的实例传入,用method.invoke(原对象,args),则执行invoke里的函数原函数。
2.创建代理实例对象
public static <T> T getProxy(Class<T> inf){
return (T) Proxy.newProxyInstance(inf.getClassLoader(),new Class[]{inf},new ProxyInvocationHandler());
}
其中inf为借口,第一个参数为借口的类加载器,第二个参数为接口的Class数组,第三个为代理的执行体。
最后根据返回的接口代理实例对象执行相应的方法即可。
/** * 被代理接口 */ public interface Myinterface { public List<Object> queryList(); public String getName(); } /** * 代理方法,Mybatis中接口无实例类, * 所以此处理并不是在方法执行前后加日志等处理 * 而是生成数据库操作执行体 */ public class MapperProxy implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //当方法名为queryList时 if (method.getName().equals("queryList")) { List list = new ArrayList(); list.add("方法名为queryList()的代理执行结果"); list.add(2); list.add("autumn"); return list; } //当放回类型为String时 if (method.getReturnType().toString().equals(String.class.toString())){ return "查询数据库返回的字符串"; } return null; } } /** * 根据接口名称获取代理实例 */ public class SqlSession { /** * 获取接口代理对象实例 * @param inf 接口 * @param <T> 该接口的代理对象实例 * @return */ public static <T> T getMapper(Class<T> inf){ return (T)Proxy.newProxyInstance(inf.getClassLoader(),new Class[]{inf},new MapperProxy()); } } /** * 测试接口无子类代理对象 */ public class Demo { public static void main(String[] args) { Myinterface inf = SqlSession.getMapper(Myinterface.class); List<Object> result = inf.queryList(); System.out.println("返回结果:"+result); System.out.println("----------------------------------------"); System.out.println("返回结果:"+inf.getName()); System.out.println("----------------------------------------"); } }
还有一种用法是传递接口的子类实例对象,在子类的实例对象方法执行前后加上其他操作(如日志处理等),传送门。
jdk动态代理原理
jdk动态代理主要是一个构造函数newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)的原理
/** * 车类接口 */ public interface Moveable { public void move(); } /** * 车类接口具体实现类 */ public class Car implements Moveable { @Override public void move() { try { Thread.sleep(new Random().nextInt(3*1000)); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("car is running..."); } } /** * 模拟jdk中的InvocationHandler接口 */ public interface InvocationHandler { public void invoke(Object o, Method m); } /** * 代理实例的处理方法 * 在原本的实例基础上进行增加功能 * 接口也可以无子类,直接通过接口方法名调用代理的invoke方法 */ public class TimeHandler implements InvocationHandler { private Object target; public TimeHandler(Object target) { super(); this.target = target; } @Override public void invoke(Object o, Method m) { try { long starttime = System.currentTimeMillis(); System.out.println("开始计时...."); m.invoke(target); long endtime = System.currentTimeMillis(); System.out.println("计时结束,用时" + (endtime - starttime) + "毫秒"); } catch (Exception e) { e.printStackTrace(); } } } public class Proxy { /** * 根据接口动态生成接口的子类对象并实现方法体,执行方法为h的invoke * @param infce 被代理接口 * @param h 被代理类的方法执行体 * @return * @throws Exception */ @SuppressWarnings("unchecked") public static Object newProxyInstance(Class infce,InvocationHandler h) throws Exception{ String rt = " "; String methodStr = ""; for(Method m : infce.getMethods()){ methodStr += " @Override" + rt + " public void " + m.getName() + "() {" + rt + " try{" + rt + " Method md = " + infce.getName() + ".class.getMethod("" + m.getName() + "");" + rt + " h.invoke(this,md);" +rt+ " }catch(Exception e){ e.printStackTrace();}" + rt + " }" ; } String str = "package com.qy.dymanic.jdkproxycode;" + rt + "import java.lang.reflect.Method;" + rt + "import com.qy.dymanic.jdkproxycode.InvocationHandler;" + rt+ "public class $Proxy0 implements " + infce.getName() + " {" + rt + " public $Proxy0(InvocationHandler h) {" + rt + " this.h = h;" + rt + " }" + rt + " private InvocationHandler h;" + rt+ methodStr + rt + "}" ; //产生代理类的java文件 String filename = System.getProperty("user.dir") +"/bin/com/qy/dymanic/jdkproxycode/$Proxy0.java"; File file = new File(filename); FileUtils.writeStringToFile(file, str); //编译 //拿到编译器 JavaCompiler complier = ToolProvider.getSystemJavaCompiler(); //文件管理者 StandardJavaFileManager fileMgr = complier.getStandardFileManager(null, null, null); //获取文件 Iterable units = fileMgr.getJavaFileObjects(filename); //编译任务 CompilationTask t = complier.getTask(null, fileMgr, null, null, null, units); //进行编译 t.call(); fileMgr.close(); //load 到内存 ClassLoader cl = ClassLoader.getSystemClassLoader(); Class c = cl.loadClass("com.qy.dymanic.jdkproxycode.$Proxy0"); Constructor ctr = c.getConstructor(InvocationHandler.class); return ctr.newInstance(h); } } /** * 模拟jdk动态代理源码 * @throws Exception */ public static void main(String[] args) throws Exception { Car car = new Car(); InvocationHandler h = new TimeHandler(car); Moveable m = (Moveable)Proxy.newProxyInstance(Moveable.class,h); m.move(); }
会在bin目录下生成两个文件
其中生成的$Proxy0.java代码如下,此类为动态生成的接口的代理实例类。
package com.qy.dymanic.jdkproxycode; import java.lang.reflect.Method; import com.qy.dymanic.jdkproxycode.InvocationHandler; public class $Proxy0 implements com.qy.dymanic.jdkproxycode.Moveable { public $Proxy0(InvocationHandler h) { this.h = h; } private InvocationHandler h; @Override public void move() { try{ Method md = com.qy.dymanic.jdkproxycode.Moveable.class.getMethod("move"); h.invoke(this,md); //这个是重点!!!InvocationHandler调用它的invoke(代理类,方法名)
}catch(Exception e){ e.printStackTrace();} } }