设计模式学习(六):代理模式

设计模式,学习,代理,模式 · 浏览次数 : 67

小编点评

**代理模式学习(六)** **代理模式**是一种结构型设计模式,用于将请求代理到特定的对象上。代理模式可以分为两种类型:静态代理和动态代理。 **静态代理** * 在代码中使用静态代理时,代理类必须实现接口或继承`InvocationHandler`接口。 * 代理类在创建目标对象之前调用`before()`方法,并在创建目标对象后调用`after()`方法。 * 静态代理需要在创建代理对象时传递目标对象的字节码。 **动态代理** * 在代码中使用动态代理时,代理类不需实现任何接口或继承任何接口。 * 动态代理在创建目标对象之前调用`before()`方法,并在创建目标对象后调用`after()`方法。 * 动态代理不需要在创建代理对象时传递目标对象的字节码。 **代理模式的优点** * 增强代码可扩展性。 * 减少代码重复性。 * 提高性能。 **代理模式的缺点** * 在创建代理对象时可能存在性能开销。 * 动态代理需要在目标对象执行方法之前和之后执行回调方法。 **代理模式的应用场景** * 监控、统计、鉴权、限流、事务、幂等。 **代理模式的实现** * 使用`Enhancer`类创建代理对象。 * 使用`MethodInterceptor`类拦截方法。 * 使用`@Autowired`注解自动wire代理对象。

正文

设计模式学习(六):代理模式

作者:Grey

原文地址:

博客园:设计模式学习(六):代理模式

CSDN:设计模式学习(六):代理模式

代理模式

代理模式是结构型模式,分为静态代理和动态代理。

静态代理

举个例子,假设需要在某个类的某段代码的前后加上日志记录,就可以通过静态代理的方式实现,代码如下

public class Main {
    public static void main(String[] args) {
        new Tank().move();
    }
}

假设需要在move方法的前后都加上日志记录,我们可以设置一个代理类

public class TankLogProxy implements Moveable {
    private Moveable m;

    public TankLogProxy(Moveable m) {
        this.m = m;
    }

    @Override
    public void move() {
        System.out.println("log before");
        m.move();
        System.out.println("log after");
    }
}

通过上述改造,原先的调用就改成了

public class Main {
    public static void main(String[] args) {
        new TankLogProxy(new Tank()).move();
    }
}

即可实现在 move 方法调用前后加入日志记录的操作。

UML图如下:

image

动态代理

JDK 自带方式

即实现InvocationHandler接口。

还是以上例说明,如果需要通过 JDK 自带的方式来完成上述功能,可以这样来做

public class MovableProxy implements InvocationHandler {
    private Movable movable;

    public MovableProxy(Movable movable) {
        this.movable = movable;
    }

    public void before() {
        System.out.println("before , do sth");
    }

    public void after() {
        System.out.println("after , do sth");
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object o = method.invoke(movable, args);
        after();
        return o;
    }
}

主方法调用的时候:

public class Main {
    public static void main(String[] args) {
        Movable tank = new Tank();

        //reflection 通过二进制字节码分析类的属性和方法

        Movable m = (Movable) Proxy.newProxyInstance(Movable.class.getClassLoader(),
                new Class[]{Movable.class},
                new MovableProxy(tank)
        );

        m.move();
        m.go();
    }
}

UML图如下:

image

Cglib

JDK 自带的方式实现动态代理需要被代理对象实现一个接口, Cglib 不需要,使用示例:

需要引入 Cglib 依赖

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>

其中被代理的 Tank 类无需实现接口

public class Tank {
    public void move() {
        System.out.println("tank move");
    }
    public void go() {
        System.out.println("tank go");
    }
}
import net.sf.cglib.proxy.Enhancer;

public class Main {

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        //设置目标类的字节码文件
        enhancer.setSuperclass(Tank.class);
        //设置回调函数
        enhancer.setCallback(new MyMethodInterceptor());

        //这里的creat方法就是正式创建代理类
        Tank m = (Tank) enhancer.create();
        m.move();
        m.go();
    }
}
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class MyMethodInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        before();
        Object o = proxy.invokeSuper(obj, args);
        after();
        return o;
    }

    public void before() {
        System.out.println("before , do sth");
    }

    public void after() {
        System.out.println("after , do sth");
    }
}

JDK 动态代理与 CGLIB 代理的区别

类型 必须要实现接口 支持拦截 public 方法 支持拦截 protected 方法 拦截默认作用域方法
JDK 动态代理
CGLIB代理

无论是 JDK 自带动态代理还是 Cglib 实现动态代理,底层都是基于ASM操作二进制码,基于Java Instrumentation机制。

代理模式的实际应用场景如下

场景一

在业务系统中开发一些非功能性需求,比如:监控、统计、鉴权、限流、事务、幂等、日志、缓存等,我们将这些附加功能与业务功能解耦,放到代理类中统一处理。

场景二

RPC 框架可以看成一种代理模式。

场景三

Spring 中的 JdkDynamicAopProxyCglibAopProxy

可以使用<aop:aspectj-autoproxy proxy-target-class="true">配置强制使用 Cglib 动态代理

UML 和 代码

UML 图

代码

更多

设计模式学习专栏

参考资料

与设计模式学习(六):代理模式相似的内容:

设计模式学习(六):代理模式

设计模式学习(六):代理模式 作者:Grey 原文地址: 博客园:设计模式学习(六):代理模式 CSDN:设计模式学习(六):代理模式 代理模式 代理模式是结构型模式,分为静态代理和动态代理。 静态代理 举个例子,假设需要在某个类的某段代码的前后加上日志记录,就可以通过静态代理的方式实现,代码如下

设计模式学习(十一):组合模式

设计模式学习(十一):组合模式 作者:Grey 原文地址: 博客园:设计模式学习(十一):组合模式 CSDN:设计模式学习(十一):组合模式 组合模式 组合模式是一种结构型模式。 组合模式中,最常用的一个用法就是目录层级的遍历,话不多说,直接上代码,主方法中 public class Main {

Spring框架中的设计模式(重点学习!!!)

# Spring中的设计模式 Spring框架中用到的设计模式有很多,以下是一些常见的设计模式: 1. 依赖注入(DI)和控制反转(IoC):这是Spring框架最核心的设计模式,它允许开发人员将对象之间的依赖关系从代码中抽离出来,由Spring容器负责管理和注入对象之间的依赖关系。 2. 工厂模式

一文带你读懂设计模式之责任链模式

翻了一下之前刚入职时候的学习笔记,发现之前在熟悉业务代码的时候曾经专门学习并整理过过设计模式中的责任链模式,之前只是对其简单了解过常用的设计模式有哪些,并未结合实例和源码深入对其探究,利用熟悉代码契机进行系统学习并整理文档如下。

深入浅出 OkHttp 源码解析及应用实践

OkHttp 在 Java 和 android 世界中被广泛使用,深入学习源代码有助于掌握软件特性和提到编程水平。本文首先从源代码入手简要分析了一个请求发起过程中的核心代码,接着通过流程图和架构图概括地介绍了OkHttp的整体结构,重点分析了拦截器的责任链模式设计,最后列举了OkHttp拦截器在项目中的实际应用。

11.1 C++ STL 应用字典与列表

C++ STL 标准模板库提供了丰富的容器和算法,这些模板可以灵活组合使用,以满足不同场景下的需求。本章内容将对前面学习的知识进行总结,并重点讲解如何灵活使用STL中的vector和map容器,以及如何结合不同的算法进行组合。通过灵活组合使用这些容器和算法,能够满足不同场景下的需求,实现高效的数据处理和操作。STL的设计思想是将数据结构和算法进行分离,使得开发者能够更加专注于解决问题,提高了代码的

C#软件架构设计原则

软件架构设计原则 学习设计原则是学习设计模式的基础。在实际的开发过程中,并不是一定要求所有的代码都遵循设计原则,而是要综合考虑人力、成本、时间、质量,不刻意追求完美,要在适当的场景遵循设计原则。这体现的是一种平衡取舍,可以帮助我们设计出更加优雅的代码结构。 分别用一句话归纳总结软件设计七大原则,如下

【.NET 深呼吸】全代码编写WPF程序

学习 Code 总有这样一个过程:入门时候比较依赖设计器、标记语言等辅助工具;等到玩熟练了就会发现纯代码写 UI 其实更高效。而且,纯代码编写也是最灵活的。Windows Forms 项目是肯定可以全代码编写的,哪怕你使用了设计器,它最后也是生成代码文件;而 WPF 就值得探索一下了。咱们知道,WP

从Kafka中学习高性能系统如何设计

相信各位小伙伴之前或多或少接触过消息队列,比较知名的包含Rocket MQ和Kafka,在京东内部使用的是自研的消息中间件JMQ,从JMQ2升级到JMQ4的也是带来了性能上的明显提升,并且JMQ4的底层也是参考Kafka去做的设计。在这里我会给大家展示Kafka它的高性能是如何设计的,大家也可以学习相关方法论将其利用在实际项目中,也许下一个顶级项目就在各位的代码中产生了。

【PB案例学习笔记】-02 目录浏览器

写在前面 这是PB案例学习笔记系列文章的第二篇,该系列文章适合具有一定PB基础的读者, 通过一个个由浅入深的编程实战案例学习,提高编程技巧,以保证小伙伴们能应付公司的各种开发需求。 文章中设计到的源码,小凡都上传到了gitee代码仓库https://gitee.com/xiezhr/pb-proje