Java动态代理详解

来源:互联网 发布:mpv mac 字幕 编辑:程序博客网 时间:2024/05/22 07:49

代理是一种设计模式,JDK的代理设计模式应用有Java动态代理。

动态代理是一种思想,委托类的方法实现通过代理类来完成。代理类和委托类实现同一个接口,即让他们实现相同的方法(因为代理类是为委托类服务的,所以必须实现委托类的方法,然后再可以在委托类前后新增加一些方法)


代理设计模式有三种角色:委托类接口,委托类实现,代理类


以上模型,代码实现:

定义接口

package com.gc.impl;public interface Subject {public void action();}


委托类实现接口
package com.gc.acion;import com.gc.impl.Subject;public class RealSubject implements Subject {@Overridepublic void action() {System.out.println("RealSubject Action Method");}}


代理类实现接口,代理实现委托类的方法并且可以在前后加上一些逻辑。

package com.gc.acion;import com.gc.impl.Subject;public class SubjectProxy implements Subject{Subject subject;public SubjectProxy(Subject subject){this.subject = subject;}@Overridepublic void action() {//before逻辑System.out.println("before RealSubject Method");//实现委托类的方法subject.action();//after逻辑System.out.println("after RealSubject Method");}}



这种编程的好处在于,对于before和after方法的公共实现不必在每一个实现类里加,只需要在公共方法里加。但是还有一个缺点,因为实现的是某一个具体的接口,所以接口不同时,每一个接口都需要实现一遍,代码也是冗余的。所以在JDK的实现中用了Java动态代理机制。


JDK的Java动态代理机制:

一般是4个角色:委托类接口,委托类实现,代理类实现和调用操作者。

两个重要的类和接口:

代理类是Proxy(类):java.lang.reflect.Proxy

调用操作者InvocationHandle(接口):java.lang.reflect.InvocationHandle

将所有的触发(invoke)真实类实现方法的动作交给调用操作者,而非代理实现类亲自去实现。

看一下Proxy源码:


重点介绍几种方法:

1、这是Proxy的带参构造方法,绑定一个调用操作者。给内部的h赋值了一个InvocationHandler 

protected Proxy(InvocationHandler arg0) {Objects.requireNonNull(arg0);this.h = arg0;}


2、ClassLoader 是类装载器,interfaces是所有真实类锁拥有的全部接口数组,返回得到一个代理类Class

public static Class<?> getProxyClass(ClassLoader arg, Class[]  interfaces)throws IllegalArgumentException {Class[] arg1 = (Class[]) arg0.clone();SecurityManager arg2 = System.getSecurityManager();if (arg2 != null) {checkProxyAccess(Reflection.getCallerClass(), arg, arg1);}return getProxyClass0(arg, arg1);}


3、传入类加载器ClassLoader, 委托类所拥有的全部接口数组Class[] interface,调用操作者InvocationHandler,返回得到一个对象,一个代理类对象,相当于上文例子中的Proxy(before、service()、after)

public static Object newProxyInstance(ClassLoader arg, Class<?>[] arg0,InvocationHandler arg1) throws IllegalArgumentException {Objects.requireNonNull(arg1);Class[] arg2 = (Class[]) arg0.clone();SecurityManager arg3 = System.getSecurityManager();if (arg3 != null) {checkProxyAccess(Reflection.getCallerClass(), arg, arg2);}Class arg4 = getProxyClass0(arg, arg2);try {if (arg3 != null) {checkNewProxyPermission(Reflection.getCallerClass(), arg4);}final Constructor arg5 = arg4.getConstructor(constructorParams);if (!Modifier.isPublic(arg4.getModifiers())) {AccessController.doPrivileged(new PrivilegedAction() {public Void run() {arg5.setAccessible(true);return null;}});}return arg5.newInstance(new Object[] { arg1 });} catch (InstantiationException | IllegalAccessException arg7) {throw new InternalError(arg7.toString(), arg7);} catch (InvocationTargetException arg8) {Throwable arg6 = arg8.getCause();if (arg6 instanceof RuntimeException) {throw (RuntimeException) arg6;} else {throw new InternalError(arg6.toString(), arg6);}} catch (NoSuchMethodException arg9) {throw new InternalError(arg9.toString(), arg9);}}

4、传入一个代理类对象,得到一个InvocationHandler调用操作者

public static InvocationHandler getInvocationHandler(Object arg)throws IllegalArgumentException {if (!isProxyClass(arg.getClass())) {throw new IllegalArgumentException("not a proxy instance");} else {Proxy arg0 = (Proxy) arg;InvocationHandler arg1 = arg0.h;if (System.getSecurityManager() != null) {Class arg2 = arg1.getClass();Class arg3 = Reflection.getCallerClass();if (ReflectUtil.needsPackageAccessCheck(arg3.getClassLoader(),arg2.getClassLoader())) {ReflectUtil.checkPackageAccess(arg2);}}return arg1;}}

动态代理类是class一种在运行时才生成的class,在生成它是必须提供一组interface给它,然后Dynamic Proxy就实现了这些interface。因为动态Proxy实现了这些接口,所以可以把Dynamic Proxy实例当成是这些interface中的任一一个来使用。

当然啦,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。因为在有了调用操作类之后,代理类本身就没有必要去亲自实现了。


看一下InvocationHandler的源码,该接口只定义有一个方法:

public interface InvocationHandler {Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable;}

在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的action(),args为该方法的参数数组。

invoke中文翻译为触发,即调用操作者代替代理类统一触发真实类的实现方法。

前面说到创建Proxy实例的时候,要绑定三个参数(ClassLoader,interface数组,InvocationHandle)

就是Proxy中实现的所有真实类的接口都交给它绑定的InvacationHandle类去触发真实类的实现动作。Proxy自己只负责调用。

 

看一个动态代理的实例:

package com.gc.impl;public interface Subject {public void action();public void hello(String s);}

package com.gc.acion;import com.gc.impl.Subject;public class RealSubject implements Subject {@Overridepublic void action() {System.out.println("RealSubject Action Method");}@Overridepublic void hello(String s) {System.out.println("RealSubject Action Method Include Args:"+s);}}

package com.gc.acion;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import com.gc.impl.Subject;public class DynamicProxy implements InvocationHandler{//要代理的真实对象private Object subject;//用构造方法给要代理的真实对象赋值public DynamicProxy(Subject subject){this.subject = subject;}@Overridepublic Object invoke(Object object, Method method, Object[] arg2)throws Throwable {//before逻辑System.out.println("before RealSubject Method");//实现委托类subject的method方法,method方法中可能传有参数为arg2method.invoke(subject, arg2);//after逻辑System.out.println("after RealSubject Method");return null;}}

package com.gc.acion;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;import com.gc.impl.Subject;public class TestDynamicProxy {public static void main(String[] args) {//创建一个真实对象Subject realsub = new RealSubject();//将真实对象绑定到Dynamic Proxy上,同时也通过构造函数给DynamicProxy中的真实对象赋值InvocationHandler handler = new DynamicProxy(realsub);//创建代理类对象Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), //InvocationHandler类加载realsub.getClass().getInterfaces(), //真实对象的类接口数组,即动态代理类实现了真实类的所有接口handler);//绑定调用操作者,把对真实类的实现方法的触发都交给InvocationHalder//代理类对象调用了真实类的实现方法,关于这些方法的触发都交给了InvocationHandler(即会实现DynamicProxy的invoke)subject.action();//对应method.invoke(subject, "");真实对象,方法subject.hello("你好");//对应method.invoke(subject, arg2);真实对象,方法和参数//总结:Proxy.newProxyInstance代理类对象负责调用,InvocationHandler调用操作者负责触发。}}




0 0