设计模式 - 结构型模式_代理模式

简介: 代理模式有点像⽼⼤和⼩弟,也有点像分销商。主要解决的是问题是为某些资源的访问、对象的类的易⽤操作上提供⽅便使⽤的代理服务。⽽这种设计思想的模式经常会出现在我们的系统中,或者你⽤到过的组件中,它们都提供给你⼀种⾮常简单易⽤的⽅式控制原本你需要编写很多代码的进⾏使⽤

@[toc]

在这里插入图片描述


结构型模式

结构型模式主要是解决如何将对象和类组装成较大的结构, 并同时保持结构的灵活和⾼效。

结构型模式包括:适配器、桥接、组合、装饰器、外观、享元、代理,这7类

在这里插入图片描述


概述

在这里插入图片描述

代理模式有点像⽼⼤和⼩弟,也有点像分销商。主要解决的是问题是为某些资源的访问、对象的类的易⽤操作上提供⽅便使⽤的代理服务

⽽这种设计思想的模式经常会出现在我们的系统中,或者你⽤到过的组件中,它们都提供给你⼀种⾮常简单易⽤的⽅式控制原本你需要编写很多代码的进⾏使⽤的服务类。

类似这样的场景还有“”

  1. 你的数据库访问层⾯经常会提供⼀个较为基础的应⽤,以此来减少应⽤服务扩容时不⾄于数据库连接数暴增。
  2. 使⽤过的⼀些中间件例如RPC框架,在拿到jar包对接⼝的描述后,中间件会在服务启动的时候⽣成对应的代理类,当调⽤接⼝的时候,实际是通过代理类发出的socket信息进⾏通过。
  3. 另外像我们常⽤的 MyBatis ,基本是定义接⼝但是不需要写实现类,就可以对 xml 或者⾃定义注解⾥的 sql 语句进⾏增删改查操作。

Case

在这里插入图片描述

模拟实现mybatis-spring中代理类⽣成部分.

对于Mybatis的使⽤中只需要定义接⼝不需要写实现类就可以完成增删改查操作,解析下来我们会通过实现⼀个这样的代理类交给spring管理的核⼼过程,来讲述代理类模式。

接下来会使⽤代理类模式来模拟实现⼀个Mybatis中对类的代理过程,也就是只需要定义接⼝,就可以关联到⽅法注解中的 sql 语句完成对数据库的操作。

前置知识:

  1. BeanDefinitionRegistryPostProcessor spring的接⼝类⽤于处理对bean的定义注册。
  1. GenericBeanDefinition 定义bean的信息,在mybatis-spring中使⽤到的是 ScannedGenericBeanDefinition 略有不同。
  2. FactoryBean ⽤于处理bean⼯⼚的类

【⼯程结构】

在这里插入图片描述

【代理模式中间件模型结构】

在这里插入图片描述

  • 此模型中涉及的类并不多,但都是抽离出来的核⼼处理类。主要的事情就是对类的代理和注册到spring中。
  • 上图中最上⾯是关于中间件的实现部分,下⾯对应的是功能的使⽤

Code

【⾃定义注解】

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Select {

    String value() default "";

}

定义了⼀个模拟mybatis-spring中的⾃定义注解,⽤于使⽤在⽅法层⾯

【Dao层接⼝】

public interface IUserDao {

    @Select("select userName from user where id = #{uId}")
    String queryUserInfo(String uId);

}

定义⼀个Dao层接⼝,并把⾃定义注解添加上。这与使⽤的mybatis组件是⼀样的。

接下来开始实现中间件功能部分


【代理类定义】


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.FactoryBean;

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

public class MapperFactoryBean<T> implements FactoryBean<T> {

    private Logger logger = LoggerFactory.getLogger(MapperFactoryBean.class);

    private Class<T> mapperInterface;

    public MapperFactoryBean(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    @Override
    public T getObject() throws Exception {
        InvocationHandler handler = (proxy, method, args) -> {
            Select select = method.getAnnotation(Select.class);
            logger.info("SQL:{}", select.value().replace("#{uId}", args[0].toString()));
            return args[0] + " xxxxxxxxxxxx!";
        };
        return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{mapperInterface}, handler);
    }

    @Override
    public Class<?> getObjectType() {
        return mapperInterface;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

}
  • mybatis源码有这样的⼀个类 MapperFactoryBean ,这⾥我们也模拟⼀个这样的类,在⾥⾯实现我们对代理类的定义。
  • 通过继承 FactoryBean ,提供bean对象,也就是⽅法 T getObject()
  • 在⽅法 getObject() 中提供类的代理以及模拟对sql语句的处理,这⾥包含了⽤户调⽤dao层⽅法时候的处理逻辑。
  • 还有最上⾯我们提供构造函数来透传需要被代理类 Class<T> mapperInterface ,在mybatis中也是使⽤这样的⽅式进⾏透传
  • 另外 getObjectType() 提供对象类型反馈,以及 isSingleton() 返回类是单例的

【将Bean定义注册到Spring容器】


  
public class RegisterBeanFactory implements BeanDefinitionRegistryPostProcessor {
    
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(MapperFactoryBean.class);
        beanDefinition.setScope("singleton");
        beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(IUserDao.class);

        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(beanDefinition, "userDao");
        BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        // left intentionally blank
    }

}
  • 将代理的bean交给spring容器管理,也就可以⾮常⽅便让我们可以获取到代理的bean。
  • GenericBeanDefinition ,⽤于定义⼀个bean的基本信息 setBeanClass(MapperFactoryBean.class); ,也包括可以透传给构造函数信息 addGenericArgumentValue(IUserDao.class);
  • 最后使⽤ BeanDefinitionReaderUtils.registerBeanDefinition ,进⾏bean的注册,也就是注册到 DefaultListableBeanFactory

接下来, 配置⽂件spring-config,配置RegisterBeanFactory bean


【单元测试 】

    @Test
    public void test_IUserDao() {
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config.xml");
        IUserDao userDao = beanFactory.getBean("userDao", IUserDao.class);
        String res = userDao.queryUserInfo("100001");
        logger.info("测试结果:{}", res);
    }
  • 通过加载Bean⼯⼚获取我们的代理类的实例对象,之后调⽤⽅法返回结果
  • 那么这个过程可以 看到我们是没有对接⼝先⼀个实现类的,⽽是使⽤代理的⽅式给接⼝⽣成⼀个实现类,并交给spring管理。

在这里插入图片描述

相关文章
|
8月前
|
设计模式 存储 缓存
【设计模式】【结构型模式】享元模式(Flyweight)
一、入门 什么是享元模式? 享元模式(Flyweight Pattern)是一种结构型设计模式,旨在通过共享对象来减少内存使用,特别适用于存在大量相似对象的情况。 它的核心思想是将对象的内在状态(不变
289 16
|
8月前
|
设计模式 Java 数据库连接
【设计模式】【结构型模式】代理模式(Proxy)
一、入门 什么是代理模式? 代理模式(Proxy Pattern)是一种结构型设计模式,允许你提供一个代理对象来控制对另一个对象的访问。 代理对象在客户端和目标对象之间起到中介作用,可以在不改变目标对
220 10
|
8月前
|
设计模式 Java 定位技术
【设计模式】【结构型模式】组合模式(Composite)
一、入门 什么是组合模式 组合模式(Composite Pattern)是一种结构型设计模式,它允许你将对象组合成树形结构来表示“部分-整体”的层次关系。组合模式使得客户端可以统一处理单个对象和组合对
264 10
|
8月前
|
关系型数据库 Java MySQL
【设计模式】【结构型模式】桥接模式(Bridge)
一、入门 什么是桥接模式? 桥接模式(Bridge Pattern)是一种结构型设计模式,核心思想是将抽象与实现分离,让它们可以独立变化。简单来说,它像一座“桥”连接了两个维度的变化,避免用继承导致代
417 10
|
8月前
|
设计模式 前端开发 Java
【设计模式】【结构型模式】适配器模式(Adpter)
一、入门 什么是适配器模式? 适配器模式是Java中常用的结构型设计模式,它的核心作用就像现实中的电源转换器一样---让原本不兼容的两个接口能够协同工作。 为什么要用适配器模式? 假设我们需要在电商系
207 10
|
8月前
|
设计模式 Java 数据库连接
【设计模式】【结构型模式】外观模式(Facde)
一、入门 什么是外观模式? 一种结构型设计模式,通过为子系统中的一组接口提供一个统一的高层接口(称为外观),来简化客户端与复杂子系统的交互过程。其本质是建立抽象层来隔离复杂度。 为什么要有外观模式?
324 9
|
8月前
|
设计模式 缓存 安全
【设计模式】【结构型模式】装饰者模式(Decorator)
一、入门 什么是装饰者模式? 装饰者模式(Decorator Pattern)是 Java 中常用的结构型设计模式,它能在不修改原有对象结构的前提下,动态地为对象添加额外的职责。 为什么要装饰者模式?
195 8
|
12月前
|
设计模式 缓存 Java
「全网最细 + 实战源码案例」设计模式——代理模式
代理模式(Proxy Pattern)是一种结构型设计模式,通过代理对象控制对目标对象的访问并添加额外功能。它分为静态代理和动态代理,后者包括JDK动态代理和CGLIB动态代理。JDK动态代理基于接口反射生成代理类,而CGLIB通过继承目标类生成子类。代理模式适用于延迟初始化、访问控制、远程服务、日志记录和缓存等场景,优点是职责分离、符合开闭原则和提高安全性,缺点是增加系统复杂性。
287 25
|
设计模式 前端开发 数据安全/隐私保护
前端必须掌握的设计模式——代理模式
代理模式(Proxy Pattern)是一种结构型设计模式,通过引入“替身”对象来间接访问真实对象,从而解耦并提升性能和安全性。例如,知名艺人复出后,经纪人作为代理筛选商单,确保只处理符合团队利益的请求。代码实现中,定义接口`IService`,艺人和经纪人都实现该接口,经纪人在访问时进行过滤和转发。代理模式常用于权限控制、性能优化等场景,如前端中的Tree-shaking和ES6的Proxy构造方法。
前端必须掌握的设计模式——代理模式
|
设计模式 缓存 安全
设计模式——代理模式
静态代理、JDK动态代理、Cglib 代理
设计模式——代理模式