设计模式之代理模式

  • 简介
    在某些场景下,我们需要增强某个对象的使用,比如我们在执行某个方法前加输出一条日志,但是我们不能直接改这个类,我们可以用代理对象来实现这个功能

  • 模式应用
    Spring AOP/日志/事务 等

  • 类图

  • 源码实现
    静态代理

jdk动态代理

点击查看代码
package designpattern.proxy.jdk_proxy;

public interface SellInterface {
    void sell();
}

点击查看代码
package designpattern.proxy.jdk_proxy;

public class Sell implements SellInterface {


    @Override
    public void sell() {
        System.out.println("sell");
    }
}

点击查看代码
package designpattern.proxy.jdk_proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Client {

    public static void main(String[] args) {
        Sell sell = new Sell();
//        Proxy.newProxyInstance(Sell.class.getClassLoader(),Sell.class.getInterfaces(),);
        SellInterface obj = (SellInterface)Proxy.newProxyInstance(sell.getClass().getClassLoader(), sell.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("invoke...");
                return method.invoke(sell,args);
            }
        });
        System.out.println(obj.getClass());
        obj.sell();

//        System.out.println(Sell.class == sell.getClass());

        while (true){

        }

    }
}

使用arthas工具查看动态代理类的源码

选择进程7

用sc命令查询jvm已加载的类

我们程序Client后运行输出的代理类名称是 com.sun.proxy.$Proxy0,在这个地方

用jad命令反编译,得到源码

源码如下:

点击查看代码
/*
 * Decompiled with CFR.
 *
 * Could not load the following classes:
 *  designpattern.proxy.jdk_proxy.SellInterface
 */
package com.sun.proxy;

import designpattern.proxy.jdk_proxy.SellInterface;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

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

    public $Proxy0(InvocationHandler invocationHandler) {
        super(invocationHandler);
    }

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

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

    public final void sell() {
        try {
            this.h.invoke(this, m3, null);
            return;
        }
        catch (Error | RuntimeException throwable) {
            throw throwable;
        }
        catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

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

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m3 = Class.forName("designpattern.proxy.jdk_proxy.SellInterface").getMethod("sell", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            return;
        }
        catch (NoSuchMethodException noSuchMethodException) {
            throw new NoSuchMethodError(noSuchMethodException.getMessage());
        }
        catch (ClassNotFoundException classNotFoundException) {
            throw new NoClassDefFoundError(classNotFoundException.getMessage());
        }
    }
}

稍微整理下源码,把toString hashCode equals这些方法删掉

点击查看代码
/*
 * Decompiled with CFR.
 *
 * Could not load the following classes:
 *  designpattern.proxy.jdk_proxy.SellInterface
 */
package com.sun.proxy;

import designpattern.proxy.jdk_proxy.SellInterface;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0
extends Proxy
implements SellInterface {
    private static Method m3;

    public $Proxy0(InvocationHandler invocationHandler) {
        super(invocationHandler);
    }



    public final void sell() {
            this.h.invoke(this, m3, null);
            return;
    }


    static {
            m3 = Class.forName("designpattern.proxy.jdk_proxy.SellInterface").getMethod("sell", new Class[0]);
            return;
    }
}

可以看到,使用代理类执行sell 方法调用的是代理类对象内成员变量h的invoke方法,而这个h对象在我们创建代理对象的时候已经创建好了

接下我们使用另一种方式实现动态代理:cglib
项目加入依赖:

点击查看代码
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>
被代理的类
点击查看代码
package designpattern.proxy.cglib_proxy;


public class Sell   {
    public void sell() {
        System.out.println("sell");
    }
}

测试程序:

点击查看代码
package designpattern.proxy.cglib_proxy;

import designpattern.proxy.static_proxy.Proxy;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class Client implements MethodInterceptor {

    //被代理的对象
    private Sell target = new Sell();

    public static void main(String[] args) {

        Client client = new Client();
        //cglib 对象 类型jdk的Proxy类
        Enhancer enhancer = new Enhancer();
        //设置要被代理的类
        enhancer.setSuperclass(Sell.class);

        enhancer.setCallback(client);

        //创建代理对象
        Sell sell = (Sell) enhancer.create();

        System.out.println(sell.getClass());
        sell.sell();


    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("cglib代理执行中...");
        method.invoke(target,objects);
        return null;
    }
}

说明:使用cglib代理需要实现MethodInterceptor 接口的intercept方法,在这个方法里写被代理对象的增强功能,使用Enhancer来创建代理对象 程序输出结果如下: ![](https://img2023.cnblogs.com/blog/1077729/202302/1077729-20230225123749606-444450967.png)

对比分析:
jdk动态代理需要被代理的类实现接口,jdk的代理对象实现了这个接口,jdk方式只能代理接口中有的方法
cglib动态代理是基于继承的方式,代理对象继承了被代理对象,如果类final修饰将不能被代理,如果方法是final修饰,将不执行代理对象的增强功能

posted @ 2023-02-25 12:03  佳琪如梦  阅读(13)  评论(0编辑  收藏  举报