>>>>>>>>>>>>>>>>>>>>>>>>>>>>>本文仅限于jdk代理,或者说是基于接口的动态代理>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Java的动态代理与两个重要的类,或者说方法有关。
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);
InvocationHandler.invoke(Object proxy, Method method, Object[] args);
举个例子,我们有一个接口Singer,有几个方法,如songList(),name(),style()等。
public interface Singer { void songList(); void name(); void style(); }
这个会有很多的实现类,如JayChou啊,JackyCheung啊等等。
像这种,存在实现类的接口,动态代理比较简单。就是创建一个InvocationHandler的实现类,让他存在一个Object属性,并且存在一个构造函数,设定这个object为具体的实现类即可。
public class SingerProxy implements InvocationHandler { private Object target; public SingerProxy(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before... something todo..."); Object invoke = method.invoke(target, args); System.out.println("After... something todo..."); return invoke; } }
测试方法:
public class Main { public static void main(String[] args) { Singer singer = new JackeyCheung(); Singer proxy = (Singer) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), singer.getClass().getInterfaces(), new SingerProxy(singer)); proxy.name(); proxy.style(); proxy.songList(); } }
InvocationHandler是一个接口,他的实现类(SingerProxy)是具体的对象的代理实现者。
他只有一个方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
proxy:在其上调用方法的代理实例
method:被代理的方法
args:被代理的方法的参数们
第二和第三个参数比较容易理解,一般调用method.invoke(target, args)就可以了。但是这个proxy参数比较费解。
实际上,他是一个类似于$Proxy0这种类的一个实例。
如果在Main中的main方法中追加一段如下的代码
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
会在项目目录下生成一个字节码文件如下所示:
public final class $Proxy0 extends Proxy implements Singer { private static Method m1; private static Method m5; private static Method m2; private static Method m3; private static Method m0; private static Method m4; 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 name() throws { try { super.h.invoke(this, m5, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } 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 void style() throws { try { super.h.invoke(this, m3, (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); } } public final void songList() throws { try { super.h.invoke(this, m4, (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")); m5 = Class.forName("dynamic.singer.Singer").getMethod("name"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m3 = Class.forName("dynamic.singer.Singer").getMethod("style"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); m4 = Class.forName("dynamic.singer.Singer").getMethod("songList"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
这个$Proxy0就是这个类的名称,这里也能看出jdk动态代理只能代理接口,因为这个类已经有一个Proxy父类,并且java不支持多继承。
这个类中的super.h就是我们自己定义个InvocationHandler的实现类。也就是去调用我们实现的invoke方法。
这里注意几个点,$Proxy0这个类把Object的toString, equals, hashcode三个方法也给代理了,toString这个方法我们最好让InvocationHandler的实现类单独实现一下,不然可能会出现堆栈溢出。
我们知道invoke方法的第一个参数是$Proxy0的一个实例了。有的时候,我们会在invoke中使用this,这个this指代的肯定是InvocationHandler的实现对象了。
那么proxy参数到底有什么用呢?
当方法没有返回值的时候,确实没有什么用途,invoke方法返回null都可以。如果存在返回值,且进行连续调用时。一个方法的调用返回自身,StringBuilder的append这种,但是StringBuilder不是一个接口所以不适用,我们可以自己制造一个SBuilder。
public interface SBuilder { SBuilder append(String s); }
这种时候,可以将proxy直接返回,方便下一次调用。实际上,不连续调用就可以了。
还有另外一种情况,就是只存在接口,没有实现类时,也可以实现动态代理。比较知名的应该就是mybatis了,只存在一个Mapper接口,还有一个对应的mapper.xml文件。
他的内部原理也是动态代理。
他是能够通过分析mapper文件,获取到mapper的方法,参数,返回值的,如果和Mapper接口中的匹配的话,就可以去调用method.invoke();
具体可以查看mybatis的MapperProxy类的invoke方法。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { if (Object.class.equals(method.getDeclaringClass())) {// Object的方法的话,toString, equals, hashcode直接调用mapperProxy的 return method.invoke(this, args); } if (this.isDefaultMethod(method)) {// 如果存在具体的实现,走default方法 return this.invokeDefaultMethod(proxy, method, args); } } catch (Throwable var5) { throw ExceptionUtil.unwrapThrowable(var5); } MapperMethod mapperMethod = this.cachedMapperMethod(method); return mapperMethod.execute(this.sqlSession, args); }
最后这块就是将每一个method都缓存一下,供后期调用。
mapperMethod的execute就是具体的实现了。内容不重要,不是动态代理的范畴,实现方式很巧妙。
public Object execute(SqlSession sqlSession, Object[] args) { Object result; switch (command.getType()) { case INSERT: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.insert(command.getName(), param)); break; } case UPDATE: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.update(command.getName(), param)); break; } case DELETE: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.delete(command.getName(), param)); break; } case SELECT: if (method.returnsVoid() && method.hasResultHandler()) { executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { result = executeForMap(sqlSession, args); } else if (method.returnsCursor()) { result = executeForCursor(sqlSession, args); } else { Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); } break; case FLUSH: result = sqlSession.flushStatements(); break; default: throw new BindingException("Unknown execution method for: " + command.getName()); } if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) { throw new BindingException("Mapper method '" + command.getName() + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ")."); } return result; }
动态代理是一个很重要的比较复杂的java内容,架构设计跟面试时,使用频率比较高。