设计模式之代理模式

发布时间:2023年12月28日

代理模式

定义

代理模式(Proxy Pattern)是一个使用率非常高的模式,其定义如下:

Provide a surrogate or placeholder for another object to control access to it.(为其他对象提供一种代理以控制对这个对象的访问。)

代理模式也叫做委托模式,它是一项基本设计技巧。许多其他的模式,如状态模式、策略模式、访问者模式本质上是在更特殊的场合采用了委托模式,而且在日常的应用中,代理模式可以提供非常好的访问控制。

优点

  1. 职责清晰

    真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件事务,附带的结果就是编程简洁清晰。

  2. 高扩展性
    具体主题角色是随时都会发生变化的,只要它实现了接口,甭管它如何变化,都逃不脱如来佛的手掌(接口),那我们的代理类完全就可以在不做任何修改的情况下使用。

  3. 智能化

    这在我们以上的讲解中还没有体现出来,不过在我们以下的动态代理章节中你就会看到代理的智能化有兴趣的读者也可以看看Struts是如何把表单元素映射到对象上的。

使用场景

我相信第一次接触到代理模式的读者肯定很郁闷,为什么要用代理呀?想想现实世界吧,打官司为什么要找个律师?因为你不想参与中间过程的是是非非,只要完成自己的答辩就成,其他的比如事前调查、事后追查都由律师来搞定,这就是为了减轻你的负担。代理模式的使用场景非常多,大家可以看看Spring AOP,这是一个非常典型的动态代理。

模式扩展

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

静态代理
  1. 虚拟代理

    虚拟代理(Virual Proxy)听着很复杂,其实非常简单,我们只要把代理模式的通用代码稍微修改一下就成为虚拟代理

    public class Proxy implements Subject {
         //要代理哪个实现类
         private Subject subject;           
         //实现接口中定义的方法
         public void request() {
                 //判断一下真实主题是否初始化
                 if(subject == null){
                       subject = new RealSubject();
                 }
                 subject.request();
         }
    }
    

    在需要的时候才初始化主题对象,可以避免被代理对象较多而引起的初始化缓慢的问题。其缺点是需要在每个方法中判断主题对象是否被创建,这就是虚拟代理,非常简单。

  2. 缓存代理

    缓存代理(Caching Proxy)是一种特殊类型的代理模式,它可以为耗时的操作或者 重复的请求提供缓存功能,从而提高程序的执行效率。缓存代理通常会在内部维护一 个缓存数据结构,如 HashMap 或者 LinkedHashMap,用来存储已经处理过的请求 及其结果。

    public class CacheBaseServiceProxy implements BaseService {
        private BaseService baseService;
        private Map<String, String> cacheMap = Collections.emptyMap();
    
        public CacheBaseServiceProxy(BaseService baseService) {
            this.baseService = baseService;
        }
    
        @Override
        public String selectById(String id) {
            String s = cacheMap.get(id);
            if (StringUtils.isBlank(s)) {
                s = baseService.selectById(id);
                cacheMap.put(id, s);
            }
            return s;
        }
    }
    
    
  3. 远程代理

    Remote Proxy是一种代理模式,用于访问位于不同地址空间的对象。远程代理 可以为本地对象提供与远程对象相同的接口,使得客户端可以透明地访问远程对象。 通常,远程代理需要处理网络通信、序列化和反序列化等细节。我们以后做rpc时也 会使用。基于Netty开发的远程rpc框架(dubbo,motan等等)都是远程代理方式。

  4. 安全代理

    Security Proxy是一种代理模式的应用,它用于控制对真实主题对象的访问。通 过安全代理,可以实现访问控制、权限验证等安全相关功能。

动态代理
  1. 基于jdk的动态代理

基于 JDK 的动态代理需要使用 java.lang.reflect.Proxy 类和java.lang.reflect.InvocationHandler 接口。我们依旧使用上述的缓存代理
的案例来实现,具体步骤如下:

先定义一个接口

public interface BaseService {
    String selectById(String id);
}

接口实现类

public class UserService implements BaseService {
    @Override
    public String selectById(String id) {
        // TODO 查库
        return "success";
    }
}

创建代理类

public class UserServiceInvocationHandler implements InvocationHandler {
    private BaseService baseService;

    public UserServiceInvocationHandler(UserService userService) {
        this.baseService = userService;
    }

    public UserServiceInvocationHandler() {
        this.baseService = new UserService();
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(baseService, args);
    }
}

测试

@Test
public void test() {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    Class[] interfaces = new Class[] {BaseService.class};
    InvocationHandler invocationHandler = new UserServiceInvocationHandler();
    BaseService baseService = (BaseService)Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
    System.out.println(baseService);
    String result = baseService.selectById("1");
    System.out.println(result);
}
  1. 基于CGLIB的动态代理
public class UserMethodInterceptor implements MethodInterceptor {
    private BaseService baseService;

    public UserMethodInterceptor() {
        this.baseService = new UserService();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        return method.invoke(baseService, args);
    }
}
@Test
public void test2() {
    // cglib通过Enhancer
    Enhancer enhancer = new Enhancer();
    // 设置他的父类
    enhancer.setSuperclass(BaseService.class);
    // 设置一个方法拦截器,用来拦截方法
    enhancer.setCallback(new UserMethodInterceptor());
    // 创建代理类
    BaseService baseService = (BaseService)enhancer.create();
    System.out.println(baseService);
    String s = baseService.selectById("123");
    System.out.println(s);
}

示例代码地址

https://gitee.com/youxiaxiaomage/java-practices/tree/master/yxxmg-gof-sample/src/main/java/com/yxxmg/gof/create/pattern/proxy

文章来源:https://blog.csdn.net/youxiaxiaomage/article/details/135262151
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。