设计模式-代理模式(静态代理,动态代理)

定义

代理模式Proxy是⼀种结构型设计模式,能够增强一些功能,不会影响到之前核心功能的流程。

结构图

1 通过实现接口的方式

2 通过继承类的方式

代理模式与装饰器模式

IT老齐白话设计模式

装饰和代理有着相似的结构, 但是其意图却⾮常不同。 这两个模式的构建都基于组合原则, 也就是说⼀个对象应该将部分⼯作委派给另⼀个对象。 两者之间的不同之处在于代理通常⾃⾏管理其服务对象的⽣命周期, ⽽装饰的⽣成则总是由客户端进⾏控制。 装饰器强调功能扩展,⽽代理模式⽤于已有功能的控制。

应用场景

原文:设计模式之美

1 非核心逻辑

代理模式最常用的一个应用场景就是,在业务系统中开发一些非功能性需求,比如:监控、统计、鉴权、限流、事务、幂等、日志。我们将这些附加功能与业务功能解耦,放到代理类中统一处理,让程序员只需要关注业务方面的开发。

2 代理模式在 RPC、缓存中的应用

RPC框架中消费者调用提供者发服务,通过代理模式可以屏蔽网络通信,编解码等多种细节。使得就像调用本地方法一样调用远程的方法。

假设我们要开发一个接口请求的缓存功能,对于某些接口请求,如果入参相同,在设定的过期时间内,直接返回缓存结果,而不用重新进行逻辑。这个时候就可以通过动态代理来实现,AOP的原理也是动态代理,缓存的功能可以通过AOP来实现。
原文:IT老齐白话设计模式

3 延迟初始化 (虚拟代理)

如果你有⼀个偶尔使⽤的重量级服务对象, ⼀直保持该对象运⾏会消耗系统资源时, 可使⽤代理模式。 你⽆需在程序启动时就创建该对象, 可将对象的初始化延迟到真正有需要的时候。

4 访问控制

如果你只希望特定客户端使⽤服务对象, 这⾥的对象可以是操作系统中⾮常重要的部分, ⽽客户端则是各种已启动的程序 (包括恶意程序), 此时可使⽤代理模式。代理可仅在客户端凭据满⾜要求时将请求传递给服务对象。

代码DEMO

1 静态代理

java 复制代码
public interface GoodService {
    Good search(String name);
}
java 复制代码
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Good {
    private String name;
    private double price;
}
java 复制代码
public class GoodServiceImpl implements GoodService{
    private static List<Good> local;
    static {
        local = new ArrayList<Good>(){{
            add(new Good("phone", 10000));
            add(new Good("watch", 1000));
            add(new Good("mac", 1000));
        }};
    }
    @Override
    public Good search(final String name) {
        Optional<Good> first = local.stream().filter(good -> good.getName().equals(name)).findFirst();
        return first.get();
    }
}

比如我们想要在查询的时候打印一些话,也就是日志功能,我们可以新建一个代理如下:

java 复制代码
public class GoodServiceProxy implements GoodService{
    private final GoodService goodService;

    public GoodServiceProxy() {
        // 静态代理在构造函数初始化
        this.goodService = new GoodServiceImpl();
    }
    @Override
    public Good search(String name) {
        System.out.println("==================== begin search ================");
        Good search = this.goodService.search(name);
        if (search != null && search.getPrice() >= 10000) {
            System.out.println("on my god, it is so expensive.");
        }
        System.out.println(JSONUtil.toJsonStr(search));
        return search;
    }
}
java 复制代码
public class Client {
    public static void main(String[] args) {
        GoodServiceProxy proxy = new GoodServiceProxy();
        Good search = proxy.search("phone");
    }
}

2 JDK动态代理

我们使用java提供的动态代理语法:

java 复制代码
public class DynamicGoodProxy implements InvocationHandler {
    private final Object targetObj;

    public DynamicGoodProxy(Object targetObj) {
        this.targetObj = targetObj;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("==================== begin search ================");
        Object search = method.invoke(targetObj, args);
        System.out.println(JSONUtil.toJsonStr(search));
        System.out.println("==================== end search ================");
        return search;
    }
}
java 复制代码
GoodService service = (GoodService) Proxy.newProxyInstance(GoodService.class.getClassLoader(), new Class[]{GoodService.class}, new DynamicGoodProxy(new GoodServiceImpl()));
Good search = service.search("phone");

原理分析:

我们首先增加JVM参数: -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true 这样生成的动态代理类会写到一个文件中。

java 复制代码
// 我们可以看到JVM给我们创建了一个类继承了Proxy 并且 实现了GoodService 
public final class $Proxy0 extends Proxy implements GoodService {
	// 通过反射把方法都放在这里,通过反射创建
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;
    // Proxy.newProxyInstance() 会先检查缓存有没有这个类有则不重新生成 没有则重新生产 生产以后会直接NEW一个生成类的对象
    // 可以看下面截图部分
    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 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);
        }
    }
    // 关键在于这里,在客户端我们拿到的对象实际上是生产的代理对象的实例,这个时候如果调用search
    // 实际上调用的是DynamicGoodProxy的Invoke方法
    public final Good search(String var1) throws  {
        try {
            return (Good)super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    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"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.liyong.learn.proxy.demo.GoodService").getMethod("search", Class.forName("java.lang.String"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}


总结:在使用JDK动态代理的时候首先JDK会给我们生产一个代理类,然后返回代理类的实例对象

3 CGLib

cglib是一款优秀的Java 字节码生成框架,它可以生成并操纵Java字节码(底层基于ASM)。

使用这个方法将CGLib生成的动态代理对象保存下来:

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, path);

java 复制代码
public class CGlibGoodServiceProxy {
    private final static Enhancer enhancer = new Enhancer();
    public static  <T> T getProxy(Class<?> targeClass, MethodInterceptor methodInterceptor) {
        enhancer.setSuperclass(targeClass);
        enhancer.setCallback(methodInterceptor);
        return (T)enhancer.create();
    }
}
java 复制代码
public class GoodServiceCallBack implements MethodInterceptor {
    /**
     *
     * @param obj "this", the enhanced object 被增强的对象
     * @param method intercepted Method 被增强的方法
     * @param args argument array; primitive types are wrapped 参数
     * @param proxy used to invoke super (non-intercepted method); may be called
     * as many times as needed
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("==================== begin search ================");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println(JSONUtil.toJsonStr(result));
        System.out.println("==================== end search ================");
        return result;
    }
}
java 复制代码
public class Client {
    public static void main(String[] args) {
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\学习\\model-design-demo\\");
       GoodService service =  CGlibGoodServiceProxy.getProxy(GoodServiceImpl.class, new GoodServiceCallBack());
       Good search = service.search("phone");
    }
}

可以看到底层是通过继承来实现的:

调用Search方法的时候实际上是转发到了GoodServiceCallBack

这个callback是通过调用set方法设置上去的:

最终转发到GoodServiceCallBack

相关推荐
dax.net7 分钟前
在C#中使用适配器Adapter模式和扩展方法解决面向的对象设计问题
设计模式·c#
郑州吴彦祖7728 分钟前
【java】数据类型与变量以及操作符
java·intellij-idea
程序员大金9 分钟前
基于SpringBoot+Vue+MySQL的在线学习交流平台
java·vue.js·spring boot·后端·学习·mysql·intellij-idea
吹老师个人app编程教学14 分钟前
阿里巴巴_java开发规范手册详解
java·开发语言
天上掉下来个程小白15 分钟前
Stream流的终结方法(一)
java·windows
天上掉下来个程小白37 分钟前
请求响应-08.响应-案例
java·服务器·前端·springboot
大白_dev37 分钟前
数据校验的总结
java·开发语言
失落的香蕉44 分钟前
Java第二阶段---10方法带参---第三节 面向对象和面向过程的区别
java·开发语言
小白黑_2161 小时前
设计模式笔记
笔记·设计模式
哎呀呀嗯呀呀1 小时前
class 031 位运算的骚操作
java·算法·位运算