【玩转23种Java设计模式】结构型模式篇:代理模式

简介: 代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
软件设计模式(Design pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。

汇总目录链接:【玩转23种Java设计模式】学习目录汇总整理

一、简介

  代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

  它的组成:

  • 抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
  • 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
  • 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

二、实例

  这次举例可能难度大了一点,因为这个是代理模式是“大厂面试热点”,所以打算通过Mybatis举例说一下。如果看懂这个,以后被问到“代理模式在什么框架中有应用?”、“Mybatis中使用了什么设计模式?怎么用的”......等等,咱们至少能答上来一部分。
  模拟实现⼀个Mybatis中对类的代理过程,只需要定义接⼝,就可以关联到⽅法注解中的 sql 语句完成对数据库的操作。简单模拟下,实际源码肯定更复杂点。

1、自定义注解(Select)

定义了⼀个模拟mybatis-spring中的自定义注解,使用在方法层面。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Select {
    String value() default "";
}
AI 代码解读

2、Dao层接口(IUserDao)

public interface IUserDao {
    @Select("select * from user where id = #{id}")
    String selectById(Integer id);
}
AI 代码解读

3、代理类(MapperFactoryBean)

@Slf4j
public class MapperFactoryBean<T> implements FactoryBean<T> {
    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);
            log.info("query sql:{}", select.value());
            log.info("param:{}", args[0]);
            return "1, 张三(模拟从数据库查询的返回)";
        };
        return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{mapperInterface}, handler);
    }

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

    @Override
    public boolean isSingleton() {
        return true;
    }
}
AI 代码解读

  MapperFactoryBean 通过继承 FactoryBean ,提供bean对象,也就是方法T getObject() 。在方法 getObject() 中提供类的代理以及模拟对sql语句的处理,这里包含了用户调用dao层方法时候的处理逻辑。

4、Bean定义注册到Spring容器(RegisterBeanFactory)

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 {
    }
}
AI 代码解读

  我们将代理的bean交给spring容器管理,以方便我们获取到代理的bean。这里是在spring中关于⼀个bean注册过程的源码。GenericBeanDefinition ,用于定义⼀个bean的基本信息,也包括可以透传给构造函数信息,最后使用BeanDefinitionReaderUtils.registerBeanDefinition,进行bean的注册,也就是注册到DefaultListableBeanFactory 中。

5、spring-mybatis.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="userDao" class="com.test.RegisterBeanFactory"/>
</beans>
AI 代码解读

6、测试

@SpringBootTest
public class ApplicationTests {

    @Test
    public void test() {
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-mybatis.xml");
        IUserDao userDao = beanFactory.getBean("userDao", IUserDao.class);
        String res = userDao.selectById(1);
        System.out.println("res = " + res);
    }

}
AI 代码解读

控制台输出:

query sqlselect * from user where id = #{id}
param:1
res = 1, 张三(模拟从数据库查询的返回)
AI 代码解读

三、总结

1、优点

  • 职责清晰。代理模式能将代理对象与真实被调用的目标对象分离,真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务。
  • 高扩展性。一定程度上降低了系统的耦合度,扩展性好。
  • 代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了中介的作用和保护了目标对象的作用。

2、缺点

  • 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
  • 实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

3、应用场景

  • 权限控制
  • AOP实现
  • Mybatis mapper
  • Spring的事务
  • 全局捕获异常
  • Rpc远程调用接口
  • 日志的采集
相关文章
【高薪程序员必看】万字长文拆解Java并发编程!(8):设计模式-享元模式设计指南
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的经典对象复用设计模式-享元模式,废话不多说让我们直接开始。
63 0
【设计模式】【结构型模式】代理模式(Proxy)
一、入门 什么是代理模式? 代理模式(Proxy Pattern)是一种结构型设计模式,允许你提供一个代理对象来控制对另一个对象的访问。 代理对象在客户端和目标对象之间起到中介作用,可以在不改变目标对
71 10
【设计模式】【结构型模式】享元模式(Flyweight)
一、入门 什么是享元模式? 享元模式(Flyweight Pattern)是一种结构型设计模式,旨在通过共享对象来减少内存使用,特别适用于存在大量相似对象的情况。 它的核心思想是将对象的内在状态(不变
108 16
【设计模式】【结构型模式】组合模式(Composite)
一、入门 什么是组合模式 组合模式(Composite Pattern)是一种结构型设计模式,它允许你将对象组合成树形结构来表示“部分-整体”的层次关系。组合模式使得客户端可以统一处理单个对象和组合对
87 10
【设计模式】【结构型模式】外观模式(Facde)
一、入门 什么是外观模式? 一种结构型设计模式,通过为子系统中的一组接口提供一个统一的高层接口(称为外观),来简化客户端与复杂子系统的交互过程。其本质是建立抽象层来隔离复杂度。 为什么要有外观模式?
84 9
【设计模式】【结构型模式】桥接模式(Bridge)
一、入门 什么是桥接模式? 桥接模式(Bridge Pattern)是一种结构型设计模式,核心思想是将抽象与实现分离,让它们可以独立变化。简单来说,它像一座“桥”连接了两个维度的变化,避免用继承导致代
154 10
【设计模式】【结构型模式】装饰者模式(Decorator)
一、入门 什么是装饰者模式? 装饰者模式(Decorator Pattern)是 Java 中常用的结构型设计模式,它能在不修改原有对象结构的前提下,动态地为对象添加额外的职责。 为什么要装饰者模式?
56 8
【设计模式】【结构型模式】适配器模式(Adpter)
一、入门 什么是适配器模式? 适配器模式是Java中常用的结构型设计模式,它的核心作用就像现实中的电源转换器一样---让原本不兼容的两个接口能够协同工作。 为什么要用适配器模式? 假设我们需要在电商系
89 10
Java 设计模式:装饰者模式(Decorator Pattern)
装饰者模式属于结构型设计模式,允许通过动态包装对象的方式为对象添加新功能,提供比继承更灵活的扩展方式。该模式通过组合替代继承,遵循开闭原则(对扩展开放,对修改关闭)。
「全网最细 + 实战源码案例」设计模式——代理模式
代理模式(Proxy Pattern)是一种结构型设计模式,通过代理对象控制对目标对象的访问并添加额外功能。它分为静态代理和动态代理,后者包括JDK动态代理和CGLIB动态代理。JDK动态代理基于接口反射生成代理类,而CGLIB通过继承目标类生成子类。代理模式适用于延迟初始化、访问控制、远程服务、日志记录和缓存等场景,优点是职责分离、符合开闭原则和提高安全性,缺点是增加系统复杂性。
145 25
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问