别看小弟工作了一段时间,但对代理的理解一直不行,甚至根本就是懵逼状态。然后今天突然开窍。就赶紧记下一些理解。
java 的动态代理主要使用java.lang.reflect.Proxy。如果自己创建一个代理类的话,就需要自定义一个class实现invocationHandler,并重写invoke方法才行,这样自定义的class 才能在invoke中创建代理并调用被代理的方法。
而invocationHandler 接口只有一个invoke方法。就是给代理对象处理业务用的。
下面在说下Proxy,该对象主要负责创建代理,创建代理对象的源码如下:
@CallerSensitivepublic 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() { 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); }}
三个参数分别是:
1、代理对象的类加载器,通过调用getParent 发现是sun.misc.Launcher$ExtClassLoader@41629346
证明是APPClassLoader,系统级的类加载器
2、是一个class对象接口数组,该数组都是多态形式的,你可以传实现类的实体对象,但该方法返回的一定是对应多态的引用对象
3、代理类对象相关联的invocationHandler,是invocationHandler对象
好了。了解完参数。该怎么创建一个对象的动态代理对象呢?
步骤如下:
1、创建一个实体接口
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);}
2、创建其实现
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); }}
3、创建与动态代理类相关的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 的接口类型的动态代理。
感觉之前理解的东西有些出入。特此补充一下:
1、实现InvocationHandler的类 是一个中介类。。负责关联动态生成的代理,并调用被代理对象的方法。
2、代理类在日志输出上都有一些特殊的标记,如:
$Proxy0
0代表第几个。多个会依次累加。
3、真正创建动态代理对象的是Proxy 的newProxyInstance方法 中的Class<?> cl = getProxyClass0(loader, intfs);这句。
4、动态代理对象 在创建时,是存放在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