设计模式-代理模式Proxy

这篇具有很好参考价值的文章主要介绍了设计模式-代理模式Proxy。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

代理模式 (Proxy)

代理设计模式(Proxy Design Pattern)是一种结构型设计模式,它为其他对象提供一个代理,以控制对这个对象的访问。代理模式可以用于实现懒加载、安全访问控制、日志记录等功能。

在设计模式中,代理模式可以分为静态代理和动态代理。静态代理是指代理类在编译时就已经确定,而动态代理是指代理类在运行时动态生成

1) 静态代理

1.a) 原理解析

在不改变原始类(或叫被代理类)代码的情况下,通过引入代理类来给原始类附加功能。

1.b) 使用场景

1.缓存代理

缓存代理通常会在内部维护一个缓存数据结构,如 HashMap 或者 LinkedHashMap,用来存储已经处理过的请求及其结果。

假设有一个数据查询接口,它从数据库或其他数据源中检索数据。在没有缓存代理的情况下,每次查询都需要访问数据库,这可能会导致较高的资源消耗和延迟。通过引入缓存代理,我们可以将查询结果存储在内存中,从而避免重复查询数据库。

public interface DataQuery {
    String query(String queryKey);
}
public class DatabaseDataQuery implements DataQuery {
    @Override
    public String query(String queryKey) {
        // 使用数据源从数据库查询数据很慢
        return "result";
    }
}

创建一个缓存代理类,它同样实现了 DataQuery 接口,并在内部使用HashMap 作为缓存:

public class DatabaseDataQueryProxy implements DataQuery {
    // 实现缓存,需要数据结构
    private Map<String, String> cache = new HashMap<>(256);

    // 你代理谁,就要持有谁
    private DatabaseDataQuery dataQuery;

    public DatabaseDataQueryProxy() {
        // 1.屏蔽被代理对象
        this.dataQuery = new DatabaseDataQuery();
    }

    @Override
    public String query(String queryKey) {
        // 2.对被代理对象的方法做增强
        // 2.1.查询缓存,命中则返回
        String result = cache.get(queryKey);
        if (result != null) {
            System.out.println("命中缓存,走缓存");
            return result;
        }

        // 2.2.未命中,则查询数据库
        result = dataQuery.query(queryKey);
        // 2.2.1.如果有结果,需要将结果保存到缓存中,再返回
        if (result != null) {
            cache.put(queryKey, result);
        }
        System.out.println("未命中,走持久层");
        return result;
    }
}
// 测试代码
@Test
void test() {
    DataQuery dataQuery = new DatabaseDataQueryProxy();

    String value = dataQuery.query("key1");
    System.out.println(value);

    value = dataQuery.query("key1");
    System.out.println(value);

    value = dataQuery.query("key2");
    System.out.println(value);
}

2.安全代理

用于控制对真实主题对象的访问。通过安全代理,可以实现访问控制、权限验证等安全相关功能。

假设我们有一个敏感数据查询接口,只有具有特定权限的用户才能访问:

3.虚拟代理

在需要时延迟创建耗时或资源密集型对象。虚拟代理在初始访问时才创建实际对象,之后将直接使用该对象。这可以避免在实际对象尚未使用的情况下就创建它,从而节省资源。

以下是一个虚拟代理的应用示例:

假设我们有一个大型图片类,它从网络加载图像。由于图像可能非常大,我们希望在需要显示时才加载它。为了实现这一点,我们可以创建一个虚拟代理来代表大型图片类。

4.远程代理

用于访问位于不同地址空间的对象。远程代理可以为本地对象提供与远程对象相同的接口,使得客户端可以透明地访问远程对象。通常,远程代理需要处理网络通信、序列化和反序列化等细节。

1.c) 静态代理步骤总结

通过前四个案例,我们也大致了解了静态代理的使用方式,其大致流程如下:

  • 1.创建一个接口,定义 代理类和被代理类 实现共同的接口
  • 2.创建被代理类,实现这个接口,并且在其中定义实现方法
  • 3.创建代理类,也要实现这个接口,同时在其中定义一个被代理类的对象作为成员变量
  • 4.在代理类中实现接口中的方法,方法中调用 被代理类 中的对应方法
  • 5.通过创建代理对象,并调用其方法,方法增强

这样,被代理类的方法就会被代理类所覆盖,实现了对被代理类的增强或修改

2) 动态代理

静态代理需要手动编写代理类代理类与被代理类实现相同的接口或继承相同的父类,对被代理对象进行包装。在程序运行前,代理类的代码就已经生成,并在程序运行时调用。静态代理的优点是简单易懂,缺点是需要手动编写代理类,代码复杂度较高,且不易扩展。

动态代理是在程序运行时动态生成代理类,无需手动编写代理类,大大降低了代码的复杂度。动态代理一般使用 Java 提供的反射机制实现,可以对任意实现了接口的类进行代理。动态代理的优点是灵活性高,可以根据需要动态生成代理类,缺点是性能相对较低,由于使用反射机制,在运行时会产生额外的开销。

2.a) 基于 JDK 的动态代理实现步骤

使用缓存代理的例子

public interface DataQuery {
    String query(String queryKey);
}
public class DatabaseDataQuery implements DataQuery {
    @Override
    public String query(String queryKey) {
        // 使用数据源从数据库查询数据很慢
        return "result";
    }
}

创建一个代理类,实现 InvocationHandler 接口,实现invoke()方法,并在其中定义一个被代理类的对象作为属性。

public class CacheInvocationHandler implements InvocationHandler {

    private Map<String, String> cache = new HashMap<>(256);

    private DatabaseDataQuery databaseDataQuery;

    public CacheInvocationHandler() {
        this.databaseDataQuery = new DatabaseDataQuery();
    }

    public CacheInvocationHandler(DatabaseDataQuery databaseDataQuery) {
        this.databaseDataQuery = databaseDataQuery;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 1.判断是哪一个方法 (只对query方法做缓存)
        String result;
        if ("query".equals(method.getName())) {
            // 2.查缓存
            // 2.1.命中直接返回
            result = cache.get(args[0].toString());
            if (result != null) {
                System.out.println("从缓存拿数据");
                return result;
            }

            // 2.2.未命中,查询数据库 (需要代理实例)
            result = (String) method.invoke(databaseDataQuery, args);

            // 3.查询到了,进行缓存
            cache.put(args[0].toString(), result);
            return result;
        }

        // 当其他的方法被调用,不希望被干预,直接调用原生的方法
        return method.invoke(databaseDataQuery, args);
    }
}

主要业务逻辑 (测试代码)

@Test
void testJdkDynamicProxy() {
    // jdk提供的代理实现,主要是使用Proxy类来实现
    // 参数1 classLoader:被代理类的类加载器
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    // 参数2 代理类需要实现的接口数组
    Class[] interfaces = new Class[]{DataQuery.class};
    // 参数3 InvocationHandler
    InvocationHandler invocationHandler = new CacheInvocationHandler();

    DataQuery dataQuery = (DataQuery) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);

    // 调用query方法时,实际上是调用了invoke()方法
    String result = dataQuery.query("key1");
    System.out.println(result);
    result = dataQuery.query("key1");
    System.out.println(result);
    result = dataQuery.query("key2");
    System.out.println(result);

    System.out.println("-----------------");
    result = dataQuery.queryAll();
    System.out.println(result);
}
2.b) 基于 CGLIB 的动态代理实现步骤

基于 CGLIB 的动态代理需要使用 net.sf.cglib.proxy.Enhancer 类和 net.sf.cglib.proxy.MethodInterceptor 接口。

1.创建一个被代理类,定义需要被代理的方法 (以DatabaseDataQuery为例)

public class DatabaseDataQuery {
    public String query(String queryKey) {
        // 使用数据源从数据库查询数据很慢
        System.out.println("正在从数据库中查询数据");
        return "result";
    }

    public String queryAll() {
        System.out.println("正在从数据库中查询数据");
        return "query All result";
    }
}

2.创建一个方法拦截器类,实现 MethodInterceptor 接口,并在其中定义一个被代理类的对象作为属性。

  • intercept 方法中,我们可以对被代理对象的方法进行增强
public class CacheMethodInterceptor implements MethodInterceptor {
    private Map<String, String> cache = new HashMap<>(256);

    private DatabaseDataQuery databaseDataQuery;

    public CacheMethodInterceptor() {
        this.databaseDataQuery = new DatabaseDataQuery();
    }

    public CacheMethodInterceptor(DatabaseDataQuery databaseDataQuery) {
        this.databaseDataQuery = databaseDataQuery;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        // 1.判断是哪一个方法
        String result;
        if ("query".equals(method.getName())) {
            // 2.查询缓存,命中则直接返回
            result = cache.get(args[0].toString());
            if (result != null) {
                System.out.println("从缓存中提取数据");
                return result;
            }

            // 3.未命中,查询数据库
            result = (String) method.invoke(databaseDataQuery, args);

            // 4.缓存到缓存中
            cache.put(args[0].toString(), result);
            return result;
        }

        return method.invoke(databaseDataQuery, args);
    }
}

3.在使用代理类时,创建被代理类的对象和代理类的对象,并使用 Enhancer.create 方法生成代理对象。

@Test
void testCgkibDynamicProxy() {
    // cglib通过enhancer
    Enhancer enhancer = new Enhancer();
    // 1.设置父类
    enhancer.setSuperclass(DatabaseDataQuery.class);
    // 2.设置一个方法拦截器,用来拦截方法
    enhancer.setCallback(new CacheMethodInterceptor());
    // 3.创建代理类
    DatabaseDataQuery databaseDataQuery = (DatabaseDataQuery) enhancer.create();

    String value = databaseDataQuery.query("key1");
    System.out.println(value);
    value = databaseDataQuery.query("key1");
    System.out.println(value);
    value = databaseDataQuery.query("key2");
    System.out.println(value);
}
2.c) Spring中aop的使用步骤

在 Spring 中,AOP(面向切面编程)提供了一种有效的方式来对程序中的多个模块进行横切关注点的处理,例如日志、事务、缓存、安全等。使用 Spring AOP,可以在程序运行时动态地将代码织入到目标对象中,从而实现对目标对象的增强。

Spring AOP 的使用步骤如下:

1.引入 AOP 相关依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <version>2.3.9.RELEASE</version>
</dependency>

2.在Main中开启自动代理@EnableAspectJAutoProxy

@SpringBootApplication
@EnableAspectJAutoProxy
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }
}

3.定义接口和实现类,并将具体实现注入容器 (以DatabaseDataQuery为例)

// 接口
public interface DataQuery {
    String query(String queryKey);
}

// 实现类
@Component
public class DatabaseDataQuery implements DataQuery {
    @Override
    public String query(String queryKey) {
        // 使用数据源从数据库查询数据很慢
        System.out.println("正在从数据库中查询数据");
        return "result";
    }
}

4.定义切面类,对方法做增强

  • @Pointcut() 对某包下的某个类的某个方法做增强:.. 代表任意方法
  • @Around() 定义增强
@Component
@Aspect
public class CacheAspectj {

    private static Map<String,String> cache = new ConcurrentHashMap<>(256);

    @Pointcut("execution(* com.dcy.structural.proxy.dynamicProxy.aop.impl.DatabaseDataQuery.query(..))")
    public void pointcut() {}

    @Around("pointcut()")
    public String around(ProceedingJoinPoint joinPoint) {

        // 1.查询缓存
        Object[] args = joinPoint.getArgs();
        String key = args[0].toString();

        // 1.1.命中则返回
        String result = cache.get(key);
        if (result != null) {
            System.out.println("数据从缓存中提取");
            return result;
        }

        // 2.未命中,查询数据库,实际上是调用被代理bean的方法
        try {
            result = joinPoint.proceed().toString();
            // 如果查询有结果,进行缓存
            cache.put(key, result);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }

        return result;
    }
}

5.测试用例文章来源地址https://www.toymoban.com/news/detail-691908.html

@SpringBootTest
public class AopTest {

    @Resource
    private DataQuery dataQuery;

    @Test
    void testSpringAop() {
        String result = dataQuery.query("key1");
        System.out.println(result);
        result = dataQuery.query("key1");
        System.out.println(result);
        result = dataQuery.query("key2");
        System.out.println(result);
    }

}

到了这里,关于设计模式-代理模式Proxy的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 设计模式之代理模式(Proxy)的C++实现

    在组件的开发过程中,有些对象由于某种原因(比如对象创建的开销很大,或者对象的一些操作需要做安全控制,或者需要进程外的访问等),会使Client使用者在操作这类对象时可能会存在问题:(1)使用者直接访问这种对象会使系统带来很多麻烦。(2)或者使用者使不能

    2024年02月12日
    浏览(31)
  • 设计模式——1_6 代理(Proxy)

    诗有可解不可解,若镜花水月勿泥其迹可也 —— 谢榛 为其他对象提供一种代理以控制对这个对象的访问 某天,你突发奇想,想做一个可以展示指定文件夹内所有图片的桌面应用。这个应用很简单,遍历文件夹,发现图片文件,把图片加载到GUI上的图片列表里,显示图片名和

    2024年01月25日
    浏览(34)
  • 设计模式之代理模式(Proxy),以C++为例,实现远程代理、虚拟代理、保护代理等。

            兄弟姐妹们好,又是好久没有更新了,今天给大家简单介绍代理模式,一个很简单的设计模式,旨在不改变原对象的情况下通过代理对象来控制对原对象的访问。代理模式根据具体情况还可以分为远程代理、虚拟代理、保护代理等,下面来介绍一下。 目录  一、代理

    2023年04月09日
    浏览(29)
  • 设计模式- 代理模式(Proxy Pattern)结构|原理|优缺点|场景|示例

                                        设计模式(分类)        设计模式(六大原则)        创建型(5种)         工厂方法         抽象工厂模式        单例模式        建造者模式        原型模式     结构型(7种)      

    2024年04月24日
    浏览(31)
  • Java设计模式 (三) 代理设计模式

    什么是代理设计模式? 代理设计模式是一种结构型设计模式,它允许创建一个代理对象,用于控制对其他对象的访问。代理模式通常用于在访问对象时添加一些附加操作,而不是直接访问真实对象。代理模式可以在不改变原始类代码的情况下,通过引入代理类来增强功能。 代

    2024年02月12日
    浏览(35)
  • Java设计模式(十三)代理模式

    一、概述 代理模式是一种结构型设计模式,它提供了一个代理对象,充当被代理对象的接口,以控制对被代理对象的访问。代理模式可以在不修改被代理对象的情况下,增加额外的功能或控制访问方式。 二、代码 以下是一个示例代码,说明代理模式的使用: 在上述代码中,

    2024年02月04日
    浏览(24)
  • 【Java设计模式005】代理模式

    由于一些特定原因某些对象不适合或者不能直接引用目标对象,这时就可以使用代理模式。代理模式为目标对象提供一个代理以控制访问对象对目标对象的访问。客户端只能直接访问代理对象,不能直接访问目标对象,这么做确保了目标对象的安全。生活中一个常见的例子就

    2024年02月12日
    浏览(31)
  • Java设计模式---单例 工厂 代理模式

    单例模式是设计模式中的一种,属于创建型模式。在软件工程中,单例模式确保一个类只有一个实例,并提供一个全局访问点。这种模式常用于那些需要频繁实例化然后引用,且创建新实例的开销较大的类,例如数据库连接池、缓存管理等。 意图 :保证一个类仅有一个实例

    2024年01月24日
    浏览(39)
  • 基于Java的设计模式 - 代理模式

    代理模式是一种使用代理对象来执行目标对象的方法并在代理对象中增强目标对象方法的一种设计模式。简单来讲就是在不修改目标对象的基础上,增强主业务逻辑的设计模式。 代理模式基本可分为三种 静态代理 JDK动态代理 CGLIB动态代理 上述简单分就是静态和动态代理,静

    2024年02月07日
    浏览(28)
  • Java 与设计模式(13):代理模式

    代理模式是一种结构型设计模式,用于在访问对象时引入一个代理对象,以控制对实际对象的访问。代理对象充当了客户端和实际对象之间的中介,客户端通过代理对象间接地访问实际对象,从而可以在访问过程中添加额外的逻辑或控制。代理模式可以提供对实际对象的保护

    2024年02月09日
    浏览(27)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包