Java代理的作用和实现?

 我来答
重庆新华电脑学校
2021-10-25 · 学动漫、设计、电竞、电商、短视频、软件等
重庆新华电脑学校
重庆新华电脑学校隶属于新华教育集团,经重庆市人力资源和社会保障局审批成立的重庆地区大规模IT人才教育基地。是国家信息化教育全国示范基地,中国IT教育十大影响力品牌学校。
向TA提问
展开全部
代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
兔震特i
2021-10-26 · TA获得超过978个赞
知道大有可为答主
回答量:3288
采纳率:77%
帮助的人:95.8万
展开全部

JDK 动态代理

动态代理的核心其实就是代理对象的生成,即 Proxy.newProxyInstance(classLoader, proxyInterface, handler)。

让我们进入newProxyInstance方法观摩下,核心代码其实就三行。

这个方法需要三个参数:

  • ClassLoader,用于加载代理类的 Loader 类,通常这个 Loader 和被代理的类是同一个 Loader 类。

  • Interfaces,是要被代理的那些那些接口。

  • InvocationHandler,就是用于执行除了被代理接口中方法之外的用户自定义的操作,也是用户需要代理的最终目的。用户调用目标方法都被代理到 InvocationHandler 类中定义的唯一方法 invoke 中。

  • //获取代理类  Class cl = getProxyClass(loader, interfaces);  

  • //获取带有InvocationHandler参数的构造方法  Constructor cons = cl.getConstructor(constructorParams);  

  • //把handler传入构造方法生成实例  return (Object) cons.newInstance(new Object[] { h });

  • 一个典型的动态代理创建对象过程可分为以下四个步骤:

    1、通过实现InvocationHandler接口创建调用处理器

  • IvocationHandler handler = new InvocationHandlerImpl(...);

  • 2、通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类

  • Class clazz = Proxy.getProxyClass(classLoader,new Class[]{...});

  • 3、通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型

  • Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});

  • 4、通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入

  • Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));

  • 为了简化对象创建过程,Proxy类中的newProxyInstance方法封装了2~4,只需两步即可完成代理对象的创建。

  • Subject proxySubject = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),

  •  new Class[]{Subject.class}, new InvocationHandlerImpl (real));

  • 生成的proxySubject继承Proxy类实现Subject接口。实现的Subject的方法实际是调用处理器的invoke方法,而invoke方法利用反射调用的是被代理对象的方法(Object result=method.invoke(proxied,args));
    重点Proxy.newProxyInstance,源码分析,会在其他文档中单独总结记录。类Proxy的getProxyClass方法调用ProxyGenerator的 generateProxyClass方法产生ProxySubject.class的二进制数据。

    创建代理对象时序图

    请点击输入图片描述

    获取代理类

    getProxyClass(loader, interfaces)方法用于获取代理类,它主要做了三件事情:

    在当前类加载器的缓存里搜索是否有代理类,没有则生成代理类并缓存在本地JVM里。

  • // 缓存的key使用接口名称生成的List  Object key = Arrays.asList(interfaceNames);  

  • synchronized (cache) {  

  •    do {  

  • Object value = cache.get(key);  

  •         // 缓存里保存了代理类的引用  if (value instanceof Reference) {  

  •    proxyClass = (Class) ((Reference) value).get();  

  • }  

  • if (proxyClass != null) {  

  • // 代理类已经存在则返回  

  •    return proxyClass;  

  • } else if (value == pendingGenerationMarker) {  

  •    // 如果代理类正在产生,则等待  

  •    try {  

  • cache.wait();  

  •    } catch (InterruptedException e) {  

  •    }  

  •    continue;  

  • } else {  

  •    //没有代理类,则标记代理准备生成  

  •    cache.put(key, pendingGenerationMarker);  

  •    break;  

  • }  

  •    } while (true);  

  • }

  • 生成并加载代理类

    代理类的生成主要是以下这两行代码:

  • //生成代理类的字节码文件并保存到硬盘中(默认不保存到硬盘)   proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);  

  • //使用类加载器将字节码加载到内存中   proxyClass = defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);  

  • 代理类的生成过程


    ProxyGenerator.generateProxyClass()方法属于sun.misc包下,Oracle并没有提供源代码,但是我们可以使用
    JD-GUI这样的反编译软件打开jre\lib\rt.jar来一探究竟,以下是其核心代码的分析。

  • //添加接口中定义的方法,此时方法体为空  for (int i = 0; i < this.interfaces.length; i++) {  

  •  localObject1 = this.interfaces[i].getMethods();  

  •  for (int k = 0; k < localObject1.length; k++) {  

  •     addProxyMethod(localObject1[k], this.interfaces[i]);  

  •  }  

  • }  

  • //添加一个带有InvocationHandler的构造方法  MethodInfo localMethodInfo = new MethodInfo("<init>", "(Ljava/lang/reflect/InvocationHandler;)V", 1);  

  • //循环生成方法体代码(省略)  //方法体里生成调用InvocationHandler的invoke方法代码。(此处有所省略)  this.cp.getInterfaceMethodRef("InvocationHandler", "invoke", "Object; Method; Object;")  

  • //将生成的字节码,写入硬盘,前面有个if判断,默认情况下不保存到硬盘。  localFileOutputStream = new FileOutputStream(ProxyGenerator.access$000(this.val$name) + ".class");  

  • localFileOutputStream.write(this.val$classFile);

  • 生成的代理类源码

    那么通过以上分析,我们可以推出动态代理为我们生成了一个这样的代理类。把方法doSomeThing的方法体修改为调用LogInvocationHandler的invoke方法。

  • public class ProxyBusiness extends Proxy implements IBusiness, IBusiness2 {  

  • private LogInvocationHandler h;  

  • @Override  public void doSomeThing2() {  

  •    try {  

  •        Method m = (h.target).getClass().getMethod("doSomeThing2",null);  

  •        h.invoke(this, m, null);  

  •    } catch (Throwable e) {  

  •        // 异常处理(略)  

  •    }  

  • }

  • @Override  public boolean doSomeThing() {  

  •    try {  

  •       Method m = (h.target).getClass().getMethod("doSomeThing", null);  

  •       return (Boolean) h.invoke(this, m, null);  

  •    } catch (Throwable e) {  

  •        // 异常处理(略)  

  •    }  

  •    return false;  

  • }  

  • public ProxyBusiness(LogInvocationHandler h) {  

  •    this.h = h;  

  • }

  • 测试代理的代码如下:

  • //测试public static void main(String[] args) {  

  •    //构建AOP的Advice  

  •    LogInvocationHandler handler = new LogInvocationHandler(new Business());  

  •    new ProxyBusiness(handler).doSomeThing();  

  •    new ProxyBusiness(handler).doSomeThing2();  

  • }

  • 下面看一个自定义代理的实现。

    被代理类接口

  • public interface Subject   {  

  •  public void doSomething();  

  • }

  • 被代理类

  • //目标对象public class RealSubject implements Subject{  public void doSomething() {

  •    System.out.println( "call doSomething()" );

  •  }

  • }

  • 调用处理器(切面)

  • public class ProxyHandler implements InvocationHandler   {  

  •  private Object proxied;  

  •  public ProxyHandler( Object proxied )   {  

  •    this.proxied = proxied;  

  •  }  

  •  public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable {  

  • //在转调具体目标对象之前,可以执行一些功能处理System.out.println( "doSomething before" );    //转调具体目标对象的方法

  •    return method.invoke( proxied, args);

  • //在转调具体目标对象之后,可以执行一些功能处理System.out.println( "doSomething after" );

  •  }

  • }

  • 测试我们的代理实现

  • public class DynamicProxy   {  

  •  public static void main( String args[] )   {  

  •    RealSubject real = new RealSubject();  

  •    Subject proxySubject = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),

  •     new Class[]{Subject.class}, new ProxyHandler(real));

  •    proxySubject.doSomething();    //write proxySubject class binary data to file  

  •    createProxyClassFile();  

  •  }  

  •  public static void createProxyClassFile()   {  

  •    String name = "ProxySubject";  

  •    byte[] data = ProxyGenerator.generateProxyClass( name, new Class[] { Subject.class } );  

  •    try{  

  •      FileOutputStream out = new FileOutputStream( name + ".class" );  

  •      out.write( data );  

  •      out.close();  

  •    }catch( Exception e ) {  

  •      e.printStackTrace();  

  •    }  

  •  }  

  • }

  • 运行结果:

  • doSomething beforecall doSomething()doSomething after

  • Proxy 接口

    Proxy 的主要静态变量

  • // 映射表:用于维护类装载器对象到其对应的代理类缓存private static Map loaderToCache = new WeakHashMap();// 标记:用于标记一个动态代理类正在被创建中private static Object pendingGenerationMarker = new Object();// 同步表:记录已经被创建的动态代理类类型,主要被方法 isProxyClass 进行相关的判断private static Map proxyClasses = Collections.synchronizedMap(new WeakHashMap());// 关联的调用处理器引用protected InvocationHandler h;

  • Proxy的构造方法// 由于 Proxy 内部从不直接调用构造函数,所以 private 类型意味着禁止任何调用private Proxy() {}// 由于 Proxy 内部从不直接调用构造函数,所以 protected 意味着只有子类可以调用protected Proxy(InvocationHandler h) {this.h = h;}

  • ProxySubject 源码

    创建的代理类 ProxySubject.class

  • import java.lang.reflect.*;  

  • public final class ProxySubject extends Proxy  implements Subject{  

  •    private static Method m1;  

  •    private static Method m0;  

  •    private static Method m3;  

  •    private static Method m2;  

  •    public ProxySubject(InvocationHandler invocationhandler){  

  •        super(invocationhandler);  

  •    }  

  •    public final boolean equals(Object obj){  

  •        try {  

  •            return ((Boolean)super.h.invoke(this, m1, new Object[] {  

  •                obj  

  •            })).booleanValue();  

  •        }catch(Error _ex) {

  •        }catch(Throwable throwable){  

  •            throw new UndeclaredThrowableException(throwable);  

  •        }  

  •    }  

  •    public final int hashCode()   {

  •        try  {  

  •            return ((Integer)super.h.invoke(this, m0, null)).intValue();  

  •        }catch(Error _ex) {

  •        }catch(Throwable throwable){  

  •            throw new UndeclaredThrowableException(throwable);  

  •        }  

  •    }  

  •    /*关键部分*/

  •    public final void doSomething()  {  

  •        try {  

  •            // Proxy类中protected InvocationHandler h;关联的调用处理器引用

  •            super.h.invoke(this, m3, null);  

  •            return;  

  •        }catch(Error _ex) {  

  •        }catch(Throwable throwable) {  

  •            throw new UndeclaredThrowableException(throwable);  

  •        }  

  •    }  

  •    public final String toString() {  

  •        try {  

  •            return (String)super.h.invoke(this, m2, null);  

  •        } catch(Error _ex) {

  •        } catch(Throwable throwable){  

  •            throw new UndeclaredThrowableException(throwable);  

  •        }  

  •    }  

  •    static{  

  •        try {  

  •            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {  

  •                Class.forName("java.lang.Object")  

  •            });  

  •            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);  

  •            m3 = Class.forName("Subject").getMethod("doSomething", new Class[0]);  

  •            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);  

  •        }catch(NoSuchMethodException nosuchmethodexception)   {  

  •            throw new NoSuchMethodError(nosuchmethodexception.getMessage());  

  •        }catch(ClassNotFoundException classnotfoundexception){  

  •            throw new NoClassDefFoundError(classnotfoundexception.getMessage());  

  •        }  

  •    }  

  • }

  • CGLib 动态代理

    动态字节码生成。使用动态字节码生成技术实现AOP原理是在运行期间目标字节码加载后,生成目标类的子类,将切面逻辑加入到子类中,所以使用Cglib实现AOP不需要基于接口。

  • public static void main(String[] args) {  

  •    byteCodeGe();  

  • }  

  • public static void byteCodeGe() {  

  •    //创建一个织入器  

  •    Enhancer enhancer = new Enhancer();  

  •    //设置父类  

  •    enhancer.setSuperclass(Business.class);  

  •    //设置需要织入的逻辑  

  •    enhancer.setCallback(new LogIntercept());  

  •    //使用织入器创建子类  

  •    IBusiness2 newBusiness = (IBusiness2) enhancer.create();  

  •    newBusiness.doSomeThing2();  

  • }  

  • /**

  • * 记录日志

  • */  public static class LogIntercept implements MethodInterceptor {  

  •    @Override  

  •    public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {  

  •        //执行原有逻辑,注意这里是invokeSuper  

  •        Object rev = proxy.invokeSuper(target, args);  

  •        //执行织入的日志  

  •        if (method.getName().equals("doSomeThing2")) {  

  •            System.out.println("记录日志");  

  •        }  

  •        return rev;  

  •    }  

  • }

本回答被提问者和网友采纳
已赞过 已踩过<
你对这个回答的评价是?
评论 收起
coocom
2021-10-25 · TA获得超过1374个赞
知道大有可为答主
回答量:1.5万
采纳率:7%
帮助的人:748万
展开全部
当编辑并运行一个Java程序时,需要同时涉及到这四种方面。使用文字编辑软件(例如记事本、写字板、UltraEdit等)或集成开发环境(Eclipse、MyEclipse等)在Java源文件中定义不同的类 ,通过调用类(这些类实现了Java API)中的方法来访问资源系统,把源文件编译生成一种二进制中间码,存储在class文件中,然后再通过运行与操作系统平台环境相对应的Java虚拟机来运行class文件,执行编译产生的字节码,调用class文件中实现的方法来满足程序的Java API调用 。
折叠
已赞过 已踩过<
你对这个回答的评价是?
评论 收起
收起 更多回答(1)
推荐律师服务: 若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询

为你推荐:

下载百度知道APP,抢鲜体验
使用百度知道APP,立即抢鲜体验。你的手机镜头里或许有别人想知道的答案。
扫描二维码下载
×

类别

我们会通过消息、邮箱等方式尽快将举报结果通知您。

说明

0/200

提交
取消

辅 助

模 式