欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java静态代理和动态代理

程序员文章站 2022-06-17 19:55:08
...

代理类的两个优点:

  • 可以隐藏委托类的实现
  • 可以实现客户与委托类间的解耦,在不修改委托类代码的情况下能够做一些额外的处理。

1静态代理

UML图如下:
Java静态代理和动态代理
ServiceInterface: 服务接口类
ServiceImpl:服务具体实现类,也就是被代理类,即委托类
Proxy:代理类。
ServiceImpl和Proxy都继承自同一个接口,proxy类通过包含一个ServiceImpl的引用从而对客户提供服务,Proxy类自身也可以扩展doSomething()的实现,从而达到代理的意义

/**
 * 代理类
 */
public class Proxy implements ServiceInterface {
    private ServiceImpl server;
    
    public BusinessAgent(ServiceImpl server){
        this.server=server;
    }

    public void doSomething { 
        System,out.println("say hello");
        System,out.println("smile");
        this.server.doSomething();
        System,out.println("say goob bye");
    } 
} 

静态代理的局限在于:必须在运行前就编写好代理类

2.动态代理

2.1 动态代理的使用

假设存在这样一个需求:存在一个ServiceImpl类,它包含了上百个方法,我们需要在每次调用ServiceImpl中的任何一个方法之前打印一条日志。
如果还是用静代理的方式实现的话,那么我们就要重新实现这上百个方法,并且还包含了大量冗余。因此动态代理排上用处了

动态代理其实就是通过Java的反射机制,对委托类的调用进行一个封装扩展。

class ServiceHandler implements InvocationHandler {
    //委托类对象;
    private ServiceImpl server;

    public ServiceHandler(ServiceImpl server) {
        this.server = server;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log.info(method.getName())
        Object result = method.invoke(server, args);
        return result;
    }
}

如上所示,ServiceHandler中包含了一个委托类的引用,客户端通过动态代理对象来调用方法时,最终其实是调用到invoke()方法,invoke()方法中通过反射来调用委托类的具体方法,动态代理的所有扩展操作也都是在invoke()方法中。

如何获取动态代理对象呢?

ServiceInterface serverProxy = (ServiceInterface) Proxy.newProxyInstance(ServiceInterface.class.getClassLoader(), new Class[]{ServiceInterface.class}, new ServiceHandler(new ServiceImpl()));

serverProxy.doSomething();

java.lang.reflect.Proxy: 这是生成代理类的主类

//创建代理对象  
static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

需要三个参数:

  • 用来加载代理类的classloader
  • 代理类需要实现的interface
  • handler对象(初始化时需要传入委托类的实例)

总结一下动态代理:

  • 代理对象是在程序运行时产生的,而不是编译期
  • 对代理对象的所有接口方法调用都会转发到InvocationHandler.invoke()方法,在invoke()方法里我们可以加入任何逻辑
  • 优点:Java动态代理可以避免静态代理带来的代码冗余的问题。
  • 缺点:Java动态代理只能针对接口创建代理,不能针对类创建代理 (因为每个动态代理类都要继承Proxy类,且Java是单继承,因此没法对针对类创建代理了)

2.2 动态代理的原理

动态代理类是在运行时生成的,并不是编译时生成。
在调用newProxyInstance生成动态代理对象之前,加入下面一段代码可以将生成的动态代理class文件保存下来

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); 

生成的动态代理class文件如下:

package com.sun.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import me.zm.ServiceInterface;

public final class $Proxy0 extends Proxy implements ServiceInterface {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    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 doSomething() throws  {
        try {
            super.h.invoke(this, m3, (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 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);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("me.zm.ServiceInterface").getMethod("doSomething");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

可以看到生成的动态代理类继承自proxy类并且实现了ServiceInterface接口。而doSomething方法的实现其实是调用了handler的invoke方法。
除此之外,还可以看到动态代理对象的equals、hashCode、toString这三个方法也都是直接调用了handler的实现

参考:
http://www.importnew.com/27772.html
https://blog.csdn.net/kisimple/article/details/43709505