【设计模式】05-代理模式

本文深入探讨了代理模式的两种实现方式:静态代理与动态代理。详细介绍了JDK动态代理的工作原理,并通过代码示例展示了如何创建动态代理类。此外,还提供了一种自定义动态代理的实现方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

代理模式有两种对象:目标对象,代理对象。客户端不直接操作目标对象,由代理对象间接调用。下面举个例子:

静态代理:

public interface TargetInterface {
    int count();
    void say(String s);
}
public class RealTarget implements TargetInterface{
    @Override
    public int count() {
        return 1;
    }

    @Override
    public void say(String s) {
        System.out.println(s);
    }
}
public class StaticProxy implements TargetInterface{

    private TargetInterface target;
    public StaticProxy(RealTarget realTarget) {
        this.target=realTarget;
    }

    @Override
    public int count() {
        System.out.println("我是代理count()");
        return target.count();
    }

    @Override
    public void say(String s) {
        target.say("我是代理:"+s);
    }
}
private static void testStaticProxy(){
        RealTarget realTarget=new RealTarget();
        StaticProxy staticProxy=new StaticProxy(realTarget);
        staticProxy.count();
        staticProxy.say("hero");
    }

结果:

我是代理count()
我是代理:hero

缺点是代理类运行之前已经确定,无法动态改变。

JDK动态代理:

public class DynamicProxyFactory implements InvocationHandler{

    private TargetInterface target;

    public TargetInterface getProxy(TargetInterface target){
        this.target=target;
        return (TargetInterface)Proxy.newProxyInstance(getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
        System.out.println("我是动态代理对象:"+proxy.getClass());
        return method.invoke(target,args);
    }
}

测试:

private static void testDynamicProxy(){
        DynamicProxyFactory dynamicProxyFactory=new DynamicProxyFactory();
        TargetInterface proxy = dynamicProxyFactory.getProxy(new RealTarget());
        System.out.println(proxy.count());
        proxy.say("dynamic");
    }

结果:

我是动态代理对象:class com.sun.proxy.$Proxy0
1
我是动态代理对象:class com.sun.proxy.$Proxy0
dynamic

特点:代理类动态生成,易于扩展。

我们从打印看到JDK的动态代理生成的代理类com.sun.proxy.$Proxy0,那么这个类是什么样子的呢。我们跟下源码发现Proxy这个类生成的代理类是ProxyGenerator的public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) 方法生成的,我们就通过这个方法生成字节码写到本地文件中,然后通过反编译工具来看一下com.sun.proxy.$Proxy0的庐山真面目。

private static void testDynamicProxyClassFile() throws Exception {
        byte[] bytes = ProxyGenerator.generateProxyClass("com.sun.proxy.$Proxy0", new Class[]{TargetInterface.class}, Modifier.FINAL | Modifier.PUBLIC);
        FileOutputStream fileOutputStream=new FileOutputStream("$Proxy0.class");
        fileOutputStream.write(bytes);
        fileOutputStream.close();
    }

通过反编译工具打开$Proxy0.class文件

package com.sun.proxy;
import com.pattern.proxy.TargetInterface;
import java.lang.reflect.*;
public final class $Proxy0 extends Proxy
    implements TargetInterface
{
    public $Proxy0(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 String toString()
    {
        try
        {
            return (String)super.h.invoke(this, m2, null);
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }
    public final int count()
    {
        try
        {
            return ((Integer)super.h.invoke(this, m3, null)).intValue();
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }
    public final void say(String s)
    {
        try
        {
            super.h.invoke(this, m4, new Object[] {
                s
            });
            return;
        }
        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);
        }
    }
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m4;
    private static Method m0;
    static 
    {
        try
        {
            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("com.pattern.proxy.TargetInterface").getMethod("count", new Class[0]);
            m4 = Class.forName("com.pattern.proxy.TargetInterface").getMethod("say", new Class[] {
                Class.forName("java.lang.String")
            });
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        }
        catch(NoSuchMethodException nosuchmethodexception)
        {
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());
        }
        catch(ClassNotFoundException classnotfoundexception)
        {
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());
        }
    }
}

我们可以看到生成的代理类的构造参数中就是需要我们实现的InvocationHandler的接口,代理类的方法调用都会委派给InvocationHandler对象。所以JDK的Proxy实际就是动态生成了一个实现了代理类接口的类,然后通过classloader加载到内存中,为以后使用。我们也可以模仿这种思路自定义一个Proxy。

public class CustomProxy {

    public static final String CLASSNAME="Custom$Proxy0";
    public static final String CLASSFULLNAME="com.pattern.proxy.custom.Custom$Proxy0";

    public static Object newProxyInstance(CustomClassLoader loader,Class<?>[] interfaces,CustomInvocationHandler h) throws Exception {
        InputStream resourceAsStream = CustomProxy.class.getResourceAsStream(CLASSNAME+".txt");
        InputStreamReader inputStreamReader=new InputStreamReader(resourceAsStream);
        BufferedReader bufferedReader=new BufferedReader(inputStreamReader);
        String source=bufferedReader.lines().reduce(new StringBuilder(),(l,r)->l.append(r).append("\n"),(l,r)->l.append(r)).toString();

        File f=new File("Custom$Proxy0.java");
        new FileOutputStream(f).write(source.getBytes());

        JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager javaFileManager = javaCompiler.getStandardFileManager(null, null, null);
        Iterable<? extends JavaFileObject> javaFileObjects = javaFileManager.getJavaFileObjects(f);
        JavaCompiler.CompilationTask task = javaCompiler.getTask(null, javaFileManager, null, null, null, javaFileObjects);
        task.call();
        javaFileManager.close();
        Class proxyClass =  loader.loadClass(CLASSNAME);
        Constructor c = proxyClass.getConstructor(CustomInvocationHandler.class);
        return c.newInstance(h);
    }

}

自定义classloader

public class CustomClassLoader extends ClassLoader{
    @Override
    protected Class<?> findClass(String name) {
        FileInputStream in = null;
        ByteArrayOutputStream out = null;
        try{
            in = new FileInputStream(CustomProxy.CLASSNAME+".class");
            out = new ByteArrayOutputStream();
            byte [] buff = new byte[1024];
            int len;
            while ((len = in.read(buff)) != -1){
                out.write(buff,0,len);
            }
            return  defineClass(CustomProxy.CLASSFULLNAME,out.toByteArray(),0,out.size());
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

自定义CustomInvocationHandler

public interface CustomInvocationHandler {

    Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
public class CustomProxyFactory implements CustomInvocationHandler {
    private TargetInterface target;

    public TargetInterface getProxy(TargetInterface target) throws Exception {
        this.target=target;
        return (TargetInterface) CustomProxy.newProxyInstance(new CustomClassLoader(),target.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
        System.out.println("我是自定义动态代理对象:"+proxy.getClass());
        return method.invoke(target,args);
    }
}

代表动态生成代理类的java代码(生成过程可以通过反射获取获取接口信息,然后通过组拼字符串的方式生成java代码,这个过程略了,直接写个txt文件Custom$Proxy0.txt代表动态生成的源代码

package com.pattern.proxy.custom;

import com.pattern.proxy.TargetInterface;

import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;


public final class Custom$Proxy0 implements TargetInterface
{

    private CustomInvocationHandler h;

    public Custom$Proxy0(CustomInvocationHandler invocationhandler)
    {
        this.h=invocationhandler;
    }

    public final boolean equals(Object obj)
    {
        try
        {
            return ((Boolean)h.invoke(this, m1, new Object[] {
                    obj
            })).booleanValue();
        }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final String toString()
    {
        try
        {
            return (String)h.invoke(this, m2, null);
        }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final int count()
    {
        try
        {
            return ((Integer)h.invoke(this, m3, null)).intValue();
        }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final void say(String s)
    {
        try
        {
            h.invoke(this, m4, new Object[] {
                    s
            });
            return;
        }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final int hashCode()
    {
        try
        {
            return ((Integer)h.invoke(this, m0, null)).intValue();
        }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m4;
    private static Method m0;

    static
    {
        try
        {
            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("com.pattern.proxy.TargetInterface").getMethod("count", new Class[0]);
            m4 = Class.forName("com.pattern.proxy.TargetInterface").getMethod("say", new Class[] {
                    Class.forName("java.lang.String")
            });
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        }
        catch(NoSuchMethodException nosuchmethodexception)
        {
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());
        }
        catch(ClassNotFoundException classnotfoundexception)
        {
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());
        }
    }
}

测试:

private static void testCustomProxy() throws Exception {
        CustomProxyFactory customProxyFactory=new CustomProxyFactory();
        TargetInterface proxy = customProxyFactory.getProxy(new RealTarget());
        System.out.println(proxy.count());
        proxy.say("custom");
    }

结果:

我是自定义动态代理对象:class com.pattern.proxy.custom.Custom$Proxy0
1
我是自定义动态代理对象:class com.pattern.proxy.custom.Custom$Proxy0
custom

将JDK动态代理生成的类保存为class文件

通过查看sun.misc.ProxyGenerator#generateProxyClass(java.lang.String, java.lang.Class<?>[], int)源码发现,还可以通过设置java系统属性sun.misc.ProxyGenerator.saveGeneratedFiles=true。

jdk8之后的设置系统属性jdk.proxy.ProxyGenerator.saveGeneratedFiles=true

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值