Java静态代理和动态代理

代理类的两个优点:

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

1静态代理

UML图如下:
在这里插入图片描述
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); //通过持有的serviceimpl 引用来反射调用被代理方法
        //还可添加其他方法,会在被代理方法执行后执行
        return result;
    }
}

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

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

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

serverProxy.doSomething();

获得到的这个serverProxy对象中也就持有了上面的serviceHandler对象的引用。

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

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

需要三个参数:

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

总结一下动态代理:

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

2.2 JDK动态代理的原理

动态代理类是在运行时生成的,并不是编译时生成。
在调用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; //equals()的Method对象
    private static Method m3;//ServiceInterface中doSomething对应的Method对象
    private static Method m2;//toStrin()的Method对象
    private static Method m0;hashCode()的Method对象

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);//可以看到需要吧Handler的引用给传递进来
    }

    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 {
        //通过调用handler的invoke方法来调用ServiceInterface接口中的方法。可以看到吧m3传进去了
            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的实现,这个也是要记住的哦,在某些情景下要重新Handler中的上述方法才对的。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值