设计模式-代理模式

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

代理模式

● 为对象提供一个代理类,增强该对象的方法,控制对这个对象的访问
● 静态代理和动态代理:静态代理就是编译的时候就已经确定,而动态代理就是运行时才会生成

静态代理的使用场景

缓存代理

● 提供数据的缓存功能,避免数据库重复查询

实践
  1. 定义数据查询的接口
public interface DataQuery {

    String query(String key);
}
  1. 接口实现类实现接口
package com.hillky.desgin_learn.Proxy;

public class DataBaseQuery implements DataQuery{
    @Override
    public String query(String key) {
        return "DataBaseQuery"+key;
    }
}
  1. 创建缓存代理类,实现接口,内部使用缓存Map,成员是被代理类
package com.hillky.desgin_learn.Proxy.cacheProxy;

import lombok.NoArgsConstructor;

import java.util.HashMap;


public class CachingDataQueryProxy implements DataQuery{

    private DataQuery dataQuery;

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

    public CachingDataQueryProxy(DataBaseQuery dataBaseQuery) {
        this.dataQuery = dataBaseQuery;
    }

    public CachingDataQueryProxy() {
        this.dataQuery=new DataBaseQuery();
    }

    @Override
    public String query(String key) {

        String result = cache.get(key);
        if(result != null){
            System.out.println("从缓存中取数据");
            return result;
        }

        result = dataQuery.query(key);
        System.out.println("从数据库取数据");
        cache.put(key,result);
        return result;
    }
}
  1. 建立测试类测试效果

安全代理

● 安全代理,可以实现访问控制、权限验证等安全相关功能。

实践
  1. 定义一个数据查询接口
package com.hillky.desgin_learn.Proxy.safeProxy;

public interface SensitiveDataQuery {

    String queryData(String userId);
}
  1. 实现真实敏感数据查询实现
package com.hillky.desgin_learn.Proxy.safeProxy;

public class SensitiveDataQueryImpl implements SensitiveDataQuery{
    @Override
    public String queryData(String userId) {
        return "SensitiveDataQuery form user :"+userId;
    }
}
  1. 安全代理类,内部进行验证
package com.hillky.desgin_learn.Proxy.safeProxy;

import lombok.Data;

@Data
public class SecurityProxy implements SensitiveDataQuery{

    private  SensitiveDataQuery sensitiveDataQuery;
    private  UserAuthenticator userAuthenticator;


    public SecurityProxy() {
        this.sensitiveDataQuery=new SensitiveDataQueryImpl();
    }

    @Override
    public String queryData(String userId) {
        if (userAuthenticator.hasPermission(userId)) {
            return sensitiveDataQuery.queryData(userId);
        } else {
            return "Access Denied: Insufficient permission for user" + userId;
        }
    }
}
  1. UserAuthenticator 类来模拟用户权限验证
package com.hillky.desgin_learn.Proxy.safeProxy;

import java.util.Arrays;
import java.util.List;

public class UserAuthenticator {

    private final List<String> authorizedUserIds;
    public UserAuthenticator() {
        // 模拟从数据库或配置文件中获取已授权的用户列表
        authorizedUserIds = Arrays.asList("user1", "user2", "user3");
    }
    public boolean hasPermission(String userId) {
        return authorizedUserIds.contains(userId);
    }
}
  1. 测试代码编写

虚拟代理

● 用于需要延迟创建耗时或资源密集型对象
● 虚拟代理在初始时访问才创建实际对象,之后将直接使用该对象

实践
  1. 定义一个图片接口
package com.hillky.desgin_learn.Proxy.vitruralProxy;

public interface Image {
    void display();
}
  1. 实现一个大型图片类
package com.hillky.desgin_learn.Proxy.vitruralProxy;

public class LargeImage implements Image{

    private  String imageUrl;


    public LargeImage(String imageUrl) {
        this.imageUrl = imageUrl;
    }

    @Override
    public void display() {
        System.out.println("Displaying image: " + imageUrl);
    }
}
  1. 虚拟代理类
package com.hillky.desgin_learn.Proxy.vitruralProxy;

public class VirtualImageProxy implements Image{

    private  String imageUrl;

    private LargeImage largeImage;

    public VirtualImageProxy(String imageUrl) {
        this.imageUrl = imageUrl;

    }

    @Override
    public void display() {
        if(largeImage==null){
            largeImage=new LargeImage(imageUrl);
        }
        largeImage.display();
    }
}
  1. 测试

可以看到虚拟代理如何实现懒加载,以减少资源消耗和提高程序性能。

远程代理

● 为本地对象提供与远程对象相同的接口,使得客户端可以透明地访问远程对象。需要做序列化反序列化,网络通信(不同语言的调用)

实践
  1. 定义一个服务接口
package com.hillky.desgin_learn.Proxy.remoteProxy;

public interface RemoteService {
    String fetchData(String dataId);
}
  1. ,实现一个远程服务类
package com.hillky.desgin_learn.Proxy.remoteProxy;

public class RemoteServiceImpl implements RemoteService{
    @Override
    public String fetchData(String dataId) {
        return "Data from remote service: " + dataId;
    }
}
  1. 创建一个远程代理类,它实现了 RemoteService 接口,并在内部处理网络通信等细节:
package com.hillky.desgin_learn.Proxy.remoteProxy;

public class RemoteServiceProxy implements RemoteService{
    private  String remoteServiceUrl;
    private RemoteService remoteService;
    

    public RemoteServiceProxy(String remoteServiceUrl) {
        this.remoteServiceUrl = remoteServiceUrl;
        this.remoteService=new RemoteServiceImpl();
    }

    @Override
    public String fetchData(String dataId) {
        // 网络通信、序列化和反序列化等逻辑
        System.out.println("Connecting to remote service at: " + remoteServiceUrl);
        // 假设我们已经获取到远程服务的数据
        String result = remoteService.fetchData(dataId);
        System.out.println("Received data from remote service.");
        return result;
    }
}
  1. 测试

请注意,为了简化代码,这里省略了实际的网 络通信、序列化和反序列化逻辑。

动态代理

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

基于 JDK 的动态代理实现步骤

  1. 定义一个接口,声明需要代理的方法:
package com.hillky.desgin_learn.Proxy.remoteProxy;

public interface RemoteService {
    String fetchData(String dataId);
}
  1. 创建一个被代理类,实现这个接口,并在其中定义实现方法:
package com.hillky.desgin_learn.Proxy.remoteProxy;

public class RemoteServiceImpl implements RemoteService{
    @Override
    public String fetchData(String dataId) {
        return "Data from remote service: " + dataId;
    }
}
  1. 创建一个代理类,实现 InvocationHandler 接口,并在其中定义一个被代理类的对象作为属性。
package com.hillky.desgin_learn.Proxy.jdkProxy;

import javafx.beans.InvalidationListener;
import javafx.beans.Observable;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedHashMap;

public class CacheInvocationHandler implements InvocationHandler {

    private DataQuery dataQuery;

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

    public CacheInvocationHandler(DataQuery databaseDataQuery) {
        this.dataQuery = databaseDataQuery;
    }

    public CacheInvocationHandler() {
        this.dataQuery=new DataBaseQuery();
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String result=null;
        if(method.getName().equals("query")){
            result = cache.get(args[0].toString());
            if(result!=null){
                System.out.println("数据从缓存重获取。");
                return result;
            }
            result = (String) method.invoke(dataQuery, args);
            cache.put(args[0].toString(),result);
            return result;
        }

        // 当其他的方法被调用,不希望被干预,直接调用原生的方法
        return method.invoke(dataQuery,args);
    }
}
  1. 测试代码,符合要求,只有query方法加了缓存
package com.hillky.desgin_learn.Proxy.jdkProxy;

import org.junit.jupiter.api.Test;

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

import static org.junit.jupiter.api.Assertions.*;

class CacheInvocationHandlerTest {

    @Test
    void test() {
        // 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);
        System.out.println("--------------------");
        result = dataQuery.query("key1");
        System.out.println(result);
        System.out.println("--------------------");
        result = dataQuery.query("key2");
        System.out.println(result);
        System.out.println("++++++++++++++++++++++++++++++++++++");
        // 事实上调用queryAll方法的使用,他是调用了invoke
        result = dataQuery.queryAll("key1");
        System.out.println(result);
        System.out.println("--------------------");
        result = dataQuery.queryAll("key1");
        System.out.println(result);
        System.out.println("--------------------");
        result = dataQuery.queryAll("key2");
        System.out.println(result);
        System.out.println("--------------------");
    }
}

spring中aop的使用步骤

● 在 Spring 中,AOP(面向切面编程)提供了一种有效的方式来对程序中的多个模块进行横切关注点的处理,例如日志、事务、缓存、安全等。从而实现对目标对象的增强

  1. 引入 AOP 相关依赖
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  1. 开启自动代理
@EnableAspectJAutoProxy
@SpringBootApplication
@EnableAspectJAutoProxy
public class DesginLearnApplication {

    public static void main(String[] args) {
        SpringApplication.run(DesginLearnApplication.class, args);
    }

}
  1. 定义接口和实现类,并将具体实现注入容器
package com.hillky.desgin_learn.Proxy.aop;

public interface DataQuery {
    String query(String queryKey);
}
package com.hillky.desgin_learn.Proxy.aop;

import org.springframework.stereotype.Component;

@Component
public class DataBaseQuery implements DataQuery{
    @Override
    public String query(String queryKey) {
        System.out.println("正在从数据库查询数据");
        return "result";
    }
}

  1. 定义切面类,对方法做增强
package com.hillky.desgin_learn.Proxy.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class CacheAspectj {

    //定义切面
    @Pointcut("execution(* com.hillky.desgin_learn.Proxy.aop.DataQuery.query(..))")
    public void pointcut(){}

    @Around("pointcut()")
    public String around(ProceedingJoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        String key = args[0].toString();
        // 1、查询缓存,命中则返回
        String result = Cache.get(key);
        if(result != null){
            System.out.println("数据从缓存中获取");
            return result;
        }
        // 未命中则去数据库查询,实际上是调用被代理bean的方法
        try {
            result = joinPoint.proceed().toString();
            // 如果查询有结果,进行缓存
            Cache.put(key,result);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
        return result;
    }

}
  1. 定义缓存
package com.hillky.desgin_learn.Proxy.aop;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class Cache {

    private static Map<String,String> map = new ConcurrentHashMap<>(256);
    public static String get(String key){
        return map.get(key);
    }
    public static void put(String key,String value){
        map.put(key, value);
    }
}
  1. 测试
package com.hillky.desgin_learn.Proxy.aop;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;

import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class CacheAspectjTest {

    @Resource
    private DataQuery dataQuery;

    @Test
    void test(){
        dataQuery.query("key1");
        dataQuery.query("key1");
        dataQuery.query("key2");
    }

}

代理和 AOP 是两个相关的概念,代理是 AOP 的一种实现方式,它们都可以 用于在程序中实现对目标对象的增强。区别在于,代理主要是针对单个对象的方法调 用进行增强,而 AOP 则是针对程序中多个模块的横切关注点进行增强。

动态代理的应用场景

动态代理是一种代理模式,它在运行时动态生成代理对象,而无需提前创建具体的代理类。
使用场景:
● 日志记录
● 性能监控
● 事务管理
● 权限验证
● 缓存
● aop
● 速率限制

一般都可以用aop方便实现文章来源地址https://www.toymoban.com/news/detail-669814.html

日志记录

  1. 引入 AOP 相关依赖
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  1. 开启自动代理
@EnableAspectJAutoProxy
@SpringBootApplication
@EnableAspectJAutoProxy
public class DesginLearnApplication {

    public static void main(String[] args) {
        SpringApplication.run(DesginLearnApplication.class, args);
    }

}
  1. 创建一个切面类(Aspect)来实现日志记录的逻辑
package com.hillky.desgin_learn.Proxy.aop;

public interface DataQuery {
    String query(String queryKey);
}
  1. 定义切面类,对方法做增强
package com.hillky.desgin_learn.Proxy.log;


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;



@Aspect
@Component
public class LoggingAspect {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    //定义切面
    @Pointcut("within(@org.springframework.web.bind.annotation.RestController *)")
    public void restControllerMethods() {
    }

    @Before("restControllerMethods()")
    public void logMethodCall(JoinPoint joinPoint){
        String className = joinPoint.getSignature().getDeclaringTypeName();
        String methodName = joinPoint.getSignature().getName();
        logger.info("Entering method [{}.{}]", className, methodName);
    }

    @AfterReturning(pointcut = "restControllerMethods()", returning = "result")
    public void logMethodReturn(JoinPoint joinPoint, Object result) {
        String className = joinPoint.getSignature().getDeclaringTypeName();
        String methodName = joinPoint.getSignature().getName();
        logger.info("Exiting method [{}.{}], return value: {}", className,
                methodName, result);
    }
}

  1. 测试接口访问看控制台输出
package com.hillky.desgin_learn.Proxy.log;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class SampleController {

    @GetMapping("/hello")
    public String hello() {
        return "Hello, World!";
    }
}
  1. 测试
package com.hillky.desgin_learn.Proxy.aop;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;

import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class CacheAspectjTest {

    @Resource
    private DataQuery dataQuery;

    @Test
    void test(){
        dataQuery.query("key1");
        dataQuery.query("key1");
        dataQuery.query("key2");
    }

}

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

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

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

相关文章

  • 设计模式 -- 代理模式

    月是一轮明镜,晶莹剔透,代表着一张白纸(啥也不懂) 央是一片海洋,海乃百川,代表着一块海绵(吸纳万物) 泽是一柄利剑,千锤百炼,代表着千百锤炼(输入输出) 月央泽,学习的一种过程,从白纸-吸收各种知识-不断输入输出变成自己的内容 希望大家一起坚持这个过程,也同样希望大家

    2023年04月08日
    浏览(37)
  • 【设计模式】代理模式

    5.1.1 概述 由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。 Java中的代理按照代理类生成时机不同又分为静态代理和动态代理。静态代理代理类在编译期就生成

    2024年02月15日
    浏览(47)
  • 设计模式——代理模式

    代理模式(Proxy Pattern)是一种结构型设计模式,它为其他对象提供一个代理以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用,可以添加额外的功能或限制对目标对象的直接访问。 抽象主题(Subject Interface): 定义了目标对象和代理对象共有的接口。

    2024年01月17日
    浏览(47)
  • 设计模式-代理模式

    ● 为对象提供一个代理类,增强该对象的方法,控制对这个对象的访问 ● 静态代理和动态代理:静态代理就是编译的时候就已经确定,而动态代理就是运行时才会生成 缓存代理 ● 提供数据的缓存功能,避免数据库重复查询 实践 定义数据查询的接口 接口实现类实现接口

    2024年02月11日
    浏览(33)
  • 设计模式(5)代理模式

    一、介绍: 1、组成结构: (1)Subject/抽象角色:定义了RealSubject和Proxy的共用接口,这样就可以在任何使用        RealSubject的地方都可以使用Proxy (2)RealSubject/真实角色:定义Proxy所代表的真实实体 (3)Proxy/代理角色:保存一个引用使得代理可以访问实体,并提供一个与

    2024年02月13日
    浏览(33)
  • 设计模式|代理模式

    ​代理模式指为其他对象提供一种代理,以控制对这个对象的访问。在某些情况下,一个对象若不能直接引用另一个对象,而代理对象可以在客户端与目标对象之间起到中介的作用。 普通代理 普通代理模式是指在代理模式中,代理对象和真实对象都实现了相同的接口或继承

    2024年02月04日
    浏览(32)
  • 【设计模式】-代理模式

    在软件开发中,经常遇到需要对某个对象进行控制或者监控的场景。而直接修改对象的代码可能使代码变得复杂且难以维护。这时,使用代理模式(Proxy Pattern)可以很好地解决这个问题。          代理模式是一种结构型设计模式, 通过引入一个代理对象来替代原始对象

    2024年02月13日
    浏览(35)
  • 设计模式二:代理模式

    1、什么是动态代理 可能很多小伙伴首次接触动态代理这个名词的时候,或者是在面试过程中被问到动态代理的时候,不能很好的描述出来,动态代理到底是个什么高大上的技术。不方,其实动态代理的使用非常广泛,例如我们平常使用的 Spring 中的 @Transactional 注解,其依赖

    2024年02月20日
    浏览(45)
  • 设计模式--代理模式

    笔记来源:尚硅谷Java设计模式(图解+框架源码剖析) 1)代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即 通过代理对象访问目标对象 2)这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能 3)被代理的对象可以

    2024年02月10日
    浏览(34)
  • 设计模式之代理模式

    当我们有对象因为安全性,不能直接对外暴露,或者是需要对对象的操作本身记录日志等信息时就可以考虑使用代理模式, 享元设计模式,包含如下元素: UML图如下: 另外,代理又分为静态代理和动态代理,静态代理就是在编译器已经确定的代理方式,即是硬编码到程序中

    2024年02月16日
    浏览(33)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包