别看小弟工做了一段时间,但对代理的理解一直不行,甚至根本就是懵逼状态。而后今天忽然开窍。就赶忙记下一些理解。html
java 的动态代理主要使用java.lang.reflect.Proxy。若是本身建立一个代理类的话,就须要自定义一个class实现invocationHandler,并重写invoke方法才行,这样自定义的class 才能在invoke中建立代理并调用被代理的方法。java
而invocationHandler 接口只有一个invoke方法。就是给代理对象处理业务用的。spring
下面在说下Proxy,该对象主要负责建立代理,建立代理对象的源码以下:数组
@CallerSensitive public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * Look up or generate the designated proxy class. */ Class<?> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ 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); } }
三个参数分别是:缓存
一、代理对象的类加载器,经过调用getParent 发现是sun.misc.Launcher$ExtClassLoader@41629346框架
证实是APPClassLoader,系统级的类加载器jvm
二、是一个class对象接口数组,该数组都是多态形式的,你能够传实现类的实体对象,但该方法返回的必定是对应多态的引用对象ide
三、代理类对象相关联的invocationHandler,是invocationHandler对象测试
好了。了解完参数。该怎么建立一个对象的动态代理对象呢?ui
步骤以下:
一、建立一个实体接口
package com.wisely.proxy; /** * DES:代理对象实体bean * Created by Reynole-白 * Date: 2017/8/26 16:27 */ public interface Person { void sing(String songName); void dance(String danceName); }
二、建立其实现
package com.wisely.proxy; /** * DES: * Created by Reynole-白 * Date: 2017/8/26 16:29 */ public class LiuDeHua implements Person { public LiuDeHua(){ System.out.println("我是华仔的无参构造器"); } @Override public void sing(String songName) { System.out.println("华仔唱:" + songName); } @Override public void dance(String danceName) { System.out.println("华仔跳:" + danceName); } }
三、建立与动态代理类相关的invocationHandler类,invoke的三个参数的定义在方法注释中有体现。
package com.wisely.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * DES:代理对象 要牛逼的 * Created by Reynole-白 * Date: 2017/8/26 16:30 */ public class LiuDeHuaProxy implements InvocationHandler { private Person pp = new LiuDeHua(); /** * 代理对象执行的invoke方法,若是想让代理对象作些逻辑操做,能够在invoke中进行编码 * @param proxy 表示代理对象,这个对象才是真正的动态代理对象 * @param method 代理对象当前执行的方法 * @param args 方法参数 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); Object obj = args[0]; if(obj instanceof String){ System.out.println("传入的参数是string类型的"); } //代理对象要作的事情 if("sing".equals(methodName)){ System.out.println("我是华仔的代理对象,找他唱歌先给钱~!!!" + args[0]); System.out.println("已收到你的10K RMB,通知华仔……"); pp.sing(args[0].toString()); }else if("dance".equals(methodName)){ System.out.println("我是华仔的代理对象,找他跳舞,先经过我这关!!~~" + args[0]); System.out.println("你已通关,通知华仔……"); pp.dance(args[0].toString()); } return null; } }
测试类:若是不是多态形式接收代理生成的Object 那么编译不经过
package com.wisely.proxy; import java.lang.reflect.Proxy; /** * DES: 代理测试类 * Created by Reynole-白 * Date: 2017/8/26 16:38 */ public class ProxyTestMain { public static void main(String[] args) { Person p = new LiuDeHua();//被代理的对象 Person p2 = new JavaFatcher(); /** * 这里代理对象的生成其实能够写到代理对象中,以匿名内部类的形式 * 这里要注意,代理对象 必需要以多态的形式定义 */ ClassLoader cl = LiuDeHuaProxy.class.getClassLoader(); System.out.println(cl.getParent()); Person personProxy = (Person) Proxy.newProxyInstance(LiuDeHuaProxy.class.getClassLoader(),p.getClass().getInterfaces(), new LiuDeHuaProxy()); personProxy.sing("冰雨"); personProxy.dance("迪斯科"); } }
运行结果以下:
我是华仔的无参构造器
sun.misc.Launcher$ExtClassLoader@41629346
我是华仔的无参构造器
传入的参数是string类型的
我是华仔的代理对象,找他唱歌先给钱~!!!冰雨
已收到你的10K RMB,通知华仔……
华仔唱:冰雨
传入的参数是string类型的
我是华仔的代理对象,找他跳舞,先经过我这关!!~~迪斯科
你已通关,通知华仔……
华仔跳:迪斯科
Process finished with exit code 0
-------------------------------------------------------------------------------------------------
以上例子,参考了http://blog.csdn.net/pangqiandou/article/details/52964066 这篇博客的例子
其使用场景 就是在大多数框架设计以及 封装设计的时候使用。通常搬砖的像我这样的猿尚未接触到。但若是想看spring这类框架的源码的话尤为是AOP ,是须要了解其动态代理的。
以上,有不对的地方,请个位大大海涵,并友情指正。拜谢!!!
下一步好好研究一下反射。
=============================================================
补充。由于小弟近期在看dubbo的源码。接触到了dubbo 的代理模式。就复习了jdk 的接口类型的动态代理。
感受以前理解的东西有些出入。特此补充一下:
一、实现InvocationHandler的类 是一个中介类。。负责关联动态生成的代理,并调用被代理对象的方法。
二、代理类在日志输出上都有一些特殊的标记,如:
$Proxy0
0表明第几个。多个会依次累加。
三、真正建立动态代理对象的是Proxy 的newProxyInstance方法 中的Class<?> cl = getProxyClass0(loader, intfs);这句。
四、动态代理对象 在建立时,是存放在jvm缓存中的class文件。反编译后为:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; import proxy.Person; public final class $Proxy0 extends Proxy implements Person { private static Method m1; private static Method m2; private static Method m3; private static Method m0; /** *注意这里是生成代理类的构造方法,方法参数为InvocationHandler类型,看到这,是否是就有点明白 *为什么代理对象调用方法都是执行InvocationHandler中的invoke方法,而InvocationHandler又持有一个 *被代理对象的实例,不由会想难道是....? 没错,就是你想的那样。 * *super(paramInvocationHandler),是调用父类Proxy的构造方法。 *父类持有:protected InvocationHandler h; *Proxy构造方法: * protected Proxy(InvocationHandler h) { * Objects.requireNonNull(h); * this.h = h; * } * */ public $Proxy0(InvocationHandler paramInvocationHandler) throws { super(paramInvocationHandler); } //这个静态块原本是在最后的,我把它拿到前面来,方便描述 static { try { //看看这儿静态块儿里面有什么,是否是找到了giveMoney方法。请记住giveMoney经过反射获得的名字m3,其余的先无论 m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m3 = Class.forName("proxy.Person").getMethod("giveMoney", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); return; } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); } } /** * *这里调用代理对象的giveMoney方法,直接就调用了InvocationHandler中的invoke方法,并把m3传了进去。 *this.h.invoke(this, m3, null);这里简单,明了。 *来,再想一想,代理对象持有一个InvocationHandler对象,InvocationHandler对象持有一个被代理的对象, *再联系到InvacationHandler中的invoke方法。嗯,就是这样。 */ public final void giveMoney() throws { try { this.h.invoke(this, m3, null); return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } //注意,这里为了节省篇幅,省去了toString,hashCode、equals方法的内容。原理和giveMoney方法一毛同样。 }
仔细观察 反编译的 动态代理class 就会明白。为什么 实现invocationHandler 类中invoke 方法的三个参数哪里来的对象。怎么用的。
以上补充 参考自:https://www.cnblogs.com/gonjan-blog/p/6685611.html