设计模式 代理模式(静态代理 动态代理) 与 Spring Aop源码分析 具体是如何创建Aop代理的

news2024/12/24 21:50:20

代理模式

代理模式是一种结构型设计模式,它通过创建一个代理对象来控制对真实对象的访问。这种模式可以用于提供额外的功能操作,或者扩展目标对象的功能。

在代理模式中,代理对象与真实对象实现相同的接口,以便在任何地方都可以使用相同的接口来调用真实对象的方法。这样做的好处是可以在不改变原始代码的情况下,增加或修改代码的行为。

根据创建代理对象的方式和时机,代理模式可以被分为静态代理、动态代理等类型。其中,静态代理是由程序员在编译时期就定义好的代理类,而动态代理则是在程序运行时期通过Java反射机制动态生成的。

在实际生活中,有许多应用了代理模式的场景,例如租房、卖房、家政等业务,通常由中介机构作为代理来进行操作。

静态代理

静态代理在编译期间就已经确定代理类的代码。具体来说,要实现静态代理需要手动编写代理类的代码,因此这种方式的灵活性相对较差,但由于代理类是直接编写的,所以其运行效率较高。

首先定义购房者的行为

/**
 * 购房者
 *
 * @author LionLi
 */
public interface Homebuyer {

    /**
     * 需求
     */
    String need();

    /**
     * 购买
     */
    void buy();
}

定义真实购房者

/**
 * 购房者 张三
 *
 * @author LionLi
 */
public class Zhangsan implements Homebuyer {

    /**
     * 需求
     */
    @Override
    public String need() {
        String need = "100平以上三室两厅两卫";
        System.out.println("张三: " + need);
        return need;
    }

    /**
     * 购买
     */
    @Override
    public void buy() {
        System.out.println("张三: 我已付款");
    }
}
/**
 * 购房者 王五
 *
 * @author LionLi
 */
public class Wangwu implements Homebuyer {

    /**
     * 需求
     */
    @Override
    public String need() {
        String need = "70平左右两室一厅";
        System.out.println("王五: " + need);
        return need;
    }

    /**
     * 购买
     */
    @Override
    public void buy() {
        System.out.println("张三: 我已付款");
    }
}

定义房产中介

/**
 * 房产中介代理人
 *
 * @author LionLi
 */
public class HouseAgentProxy implements Homebuyer {
    private Homebuyer homebuyer;

    public HouseAgentProxy(Homebuyer homebuyer) {
        this.homebuyer = homebuyer;
    }

    @Override
    public String need() {
        System.out.println("中介: 你对房子有什么需求 放心交给我");
        String need = homebuyer.need();
        System.out.println("中介: 寻找房源中........");
        System.out.println("中介: 寻找房源中........");
        System.out.println("中介: 寻找房源中........");
        String str = "中介: 为您找到" + need + "的房子";
        System.out.println(str);
        return str;
    }

    @Override
    public void buy() {
        System.out.println("中介: 请支付购买房子");
        homebuyer.buy();
        System.out.println("中介: 合同生效中.....");
        System.out.println("中介: 房证办理中.....");
        System.out.println("中介: 恭喜您 这套房子属于您了");
    }
}

测试

/**
 * @author LionLi
 */
public class Test {

    public static void main(String[] args) {
        Homebuyer zhangsan = new Zhangsan();
        HouseAgentProxy agent1 = new HouseAgentProxy(zhangsan);
        agent1.need();
        agent1.buy();
        System.out.println("-----------------------------");
        Homebuyer wangwu = new Wangwu();
        HouseAgentProxy agent2 = new HouseAgentProxy(wangwu);
        agent2.need();
        agent2.buy();
    }
}

两位购房者分别根据需求在中介的带领下买到了房子 真是可喜可贺啊

动态代理

动态代理允许在运行时动态地创建代理对象。代理对象可以在调用实际对象的方法前后执行一些额外的操作,比如日志记录、权限检查等。

动态代理的实现方式有两种:基于接口和基于继承。基于接口的方式是最常用的,它使用Java的反射机制来实现代理对象。基于继承的方式则需要创建一个实现了目标类接口的子类,并重写其中的方法。

优点: 可以降低系统的耦合度,提高代码的可维护性和可扩展性。
缺点: 需要使用反射机制,性能比静态代理略低。

首先定义购房者的行为与实际购房者

使用上方代码不变

定义房产中介

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

/**
 * 动态房产中介代理人
 *
 * @author LionLi
 */
public class DynamicHouseAgentProxy implements InvocationHandler {
    private Homebuyer homebuyer;

    public Homebuyer getInstance(Homebuyer homebuyer) {
        this.homebuyer = homebuyer;
        return (Homebuyer) Proxy.newProxyInstance(homebuyer.getClass().getClassLoader(), homebuyer.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().equals("need")) {
            System.out.println("中介: 你对房子有什么需求 放心交给我");
            String need = (String) method.invoke(homebuyer, args);
            System.out.println("中介: 寻找房源中........");
            System.out.println("中介: 寻找房源中........");
            System.out.println("中介: 寻找房源中........");
            String str = "中介: 为您找到" + need + "的房子";
            System.out.println(str);
            return str;
        } else if (method.getName().equals("buy")) {
            System.out.println("中介: 请支付购买房子");
            Object invoke = method.invoke(homebuyer, args);
            System.out.println("中介: 合同生效中.....");
            System.out.println("中介: 房证办理中.....");
            System.out.println("中介: 恭喜您 这套房子属于您了");
            return invoke;
        }
        return null;
    }
}

测试

/**
 * @author LionLi
 */
public class Test {

    public static void main(String[] args) {
        DynamicHouseAgentProxy agent = new DynamicHouseAgentProxy();
        Homebuyer zhangsan = new Zhangsan();
        Homebuyer proxy1 = agent.getInstance(zhangsan);
        proxy1.need();
        proxy1.buy();
        System.out.println("-----------------------------");
        Homebuyer wangwu = new Wangwu();
        Homebuyer proxy2 = agent.getInstance(wangwu);
        proxy2.need();
        proxy2.buy();
    }

}


结果不变 两位购房者成功买到房子

Cglib动态代理

其他代码不变只变更中介类

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * 动态房产中介代理人
 *
 * 这里由于cglib已经不支持jdk17了 所以我们使用spring内部自带的cglib工具
 *
 * @author LionLi
 */
public class DynamicHouseAgentProxy implements MethodInterceptor {

    public Homebuyer getInstance(Homebuyer homebuyer) {
        Enhancer enhancer = new Enhancer();
        // 设置继承父类
        enhancer.setSuperclass(homebuyer.getClass());
        // 设置回调
        enhancer.setCallback(this);
        return (Homebuyer) enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        if (method.getName().equals("need")) {
            System.out.println("中介: 你对房子有什么需求 放心交给我");
            String need = (String) proxy.invokeSuper(obj, args);
            System.out.println("中介: 寻找房源中........");
            System.out.println("中介: 寻找房源中........");
            System.out.println("中介: 寻找房源中........");
            String str = "中介: 为您找到" + need + "的房子";
            System.out.println(str);
            return str;
        } else if (method.getName().equals("buy")) {
            System.out.println("中介: 请支付购买房子");
            Object invoke = proxy.invokeSuper(obj, args);
            System.out.println("中介: 合同生效中.....");
            System.out.println("中介: 房证办理中.....");
            System.out.println("中介: 恭喜您 这套房子属于您了");
            return invoke;
        }
        return null;
    }
}

Spring中代理模式的应用

要说起Spring中的代理模式 要首当其冲的肯定是AOP了 接下来我们来看看在AOP中是如何使用代理模式的

这里涉及到一个注解 学过AOP的都知道 如果需要开启AOP那么需要增加这个注解

通过搜索找到 AspectJAutoProxyRegistrar 类, 该方法的作用就是往Spring容器中添加一个 BeanDefinition 类型为 AnnotationAwareAspectJAutoProxyCreator 用于创建代理对象


接下来我们进入 AnnotationAwareAspectJAutoProxyCreator 看看是如何创建代理的

由于 AnnotationAwareAspectJAutoProxyCreator 中并没有创建代理的方法 那么我们只能向上去父类里找, 通过不断的探索在 AnnotationAwareAspectJAutoProxyCreator -> AspectJAwareAdvisorAutoProxyCreator -> AbstractAdvisorAutoProxyCreator -> AbstractAutoProxyCreator 抽象类 AbstractAutoProxyCreator 中找到了 createProxy 方法


此方法是bean初始化的前置处理器 postProcessBeforeInstantiation 中使用的, 也就是说bean在初始化之前, 代理对象就已经创建好了

进入 createProxy 方法

进入 buildProxy 方法


这里我们可以看出 实际的代理对象是通过 ProxyFactory 工厂的 getProxyClassgetProxy 两个方法创建的

我们来看一下这两个方法具体实现

通过 getProxyClass 方法一直往下点 找到最终创建方法 DefaultAopProxyFactory#createAopProxy 为止


最后我们来分析最终是如何创建代理对象的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1382040.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

爬虫补环境jsdom、proxy、Selenium案例:某条

声明: 该文章为学习使用,严禁用于商业用途和非法用途,违者后果自负,由此产生的一切后果均与作者无关 一、简介 爬虫逆向补环境的目的是为了模拟正常用户的行为,使爬虫看起来更像是一个真实的用户在浏览网站。这样可以…

OceanBase 4.2特性解读:Show Trace全链路跟踪,助力快速问题定位与精准诊断

在分布式数据库环境下,慢 SQL 诊断是运维人员面临的一大挑战。在无法及时发现问题根本原因的情况下,可能会严重影响用户体验,甚至会导致业务服务不可用。相对于单机数据库,分布式数据库系统涉及多个节点、多组件的协同工作&#x…

Python之jieba分词相关介绍

1.jieba分词的安装 直接在cmd窗口当中pip install即可 2.jieba分词的介绍 jieba分词是目前比较好的中文分词组件之一,jieba分词支持三种模式的分词(精确模式、全模式、搜索引擎模式),并且支持自定义词典(这一点在特定的领域很重要,有时候…

【计算机网络】内容整理

概述 分组交换 分组交换则采用存储转发(整个包必须到达路由器,然后才能在下一个链路上传输)技术。 在发送端,先把较长的报文划分成较短的、固定长度的数据段。 电路交换 在端系统间通信会话期间,预留了端系统间沿路径通信所需…

开源云原生安全的现状

近年来,人们非常重视软件供应链的安全。尤其令人担忧的是开源软件发行版中固有的风险越来越多。这引发了围绕云原生开源安全的大量开发,其形式包括软件物料清单 (SBOM)、旨在验证 OSS 包来源的项目等。 许多组织循环使用大型开源包,但只使用…

第 3 场 小白入门赛(1~6) + 第 3 场 强者挑战赛 (1 ~ 5)

第 3 场 小白入门赛 1、厉不厉害你坤哥(暴力) 2、思维 3、暴力,前缀和,贪心 4、二分 5、DP 6、容斥,双指针 第 3 场 强者挑战赛 2、BFS 5、树上倍增求第k祖先 1. 召唤神坤 题意: 可以发现,如果我…

2023 IoTDB Summit:天谋科技高级开发工程师苏宇荣《汇其流:如何用 IoTDB 流处理框架玩转端边云融合》...

12 月 3 日,2023 IoTDB 用户大会在北京成功举行,收获强烈反响。本次峰会汇集了超 20 位大咖嘉宾带来工业互联网行业、技术、应用方向的精彩议题,多位学术泰斗、企业代表、开发者,深度分享了工业物联网时序数据库 IoTDB 的技术创新…

Git相关3 —— 命令及添加Gitee的公钥

1.Git相关命令1 -- 工作目录、暂存区、本地仓库、 使用平台有:cmd、Git bash、VSCode window系统修改VSCode默认终端为git bash git init 初始化 --- 新增.git 文件夹 git status 查看 文件/文件夹 状态 git add 需要追踪的文件名/文件夹名 提交到暂存区 git add…

【JavaSE语法】图书管理系统实现详解

图片出处:The worlds biggest drone photo and video sharing platform | SkyPixel.com 导言 在学完JavaSE语法后,我们就可以去尝试写一个简单的图书管理系统来进一步提升我们面对对象编程的思想。在该系统中会涉及到数组,接口,封…

OceanBase架构概览

了解一个系统或软件,比较好的一种方式是了解其架构,下图是官网上的架构图,基于V 4.2.1版本 OceanBase 使用通用服务器硬件,依赖本地存储,分布式部署在多个服务器上,每个服务器都是对等的,数据库…

YOLOv8改进 | 注意力篇 | 实现级联群体注意力机制CGAttention (全网首发)

一、本文介绍 本文给大家带来的改进机制是实现级联群体注意力机制CascadedGroupAttention,其主要思想为增强输入到注意力头的特征的多样性。与以前的自注意力不同,它为每个头提供不同的输入分割,并跨头级联输出特征。这种方法不仅减少了多头注意力中的计算冗余,而且通过增…

【国内访问github不稳定】可以尝试fastgithub解决这个问题

1、下载 https://github.com/dotnetcore/FastGithub https://github.com/dotnetcore/FastGithub/releases 官网下载即可,比如,我用的是这个:fastgithub_osx-x64.zip(点这里下载) 2、安装 如下图双击启动即可 3、…

【SSM框架】SpringMVC

SpringMVC简介 SpringMVC概述 SpringMvC是一种基于Java实现MVC模型的轻量级web框架 SpringMVC技术与Servlet技术功能等同&#xff0c;用于表现层功能开发 SpringMVC入门 1、导入坐标 <dependency><groupId>javax.servlet</groupId><artifactId>ja…

Docker安装Odoo17

Docker安装Odoo 前言所需环境安装步骤登录Odoo 配置数据库 前言 Odoo是一个开源的ERP框架&#xff0c;它提供了一套完整的、可定制的、模块化的企业管理软件解决方案。以下是Odoo的主要特点&#xff1a; 模块化设计&#xff1a;Odoo的各个功能都以模块的形式提供&#xff0c;包…

三端负电源电压调节器79LXX,具有一系列固定电压输出,适用于小于100mA电源供给的场合

79LXX系列三端负电源电压调节器是单片双极型线性集成电路&#xff0c;采用TO92、SOT89-3的封装形式封装&#xff0c;有一系列固定的电压输出&#xff0c;适用于小于100mA电源供给的场合。 主要特点&#xff1a; 最大输出电流为100mA 固定输出电压分别为-5V、-6V、-8V、-9V、-10…

Windows无法登录管理路由器故障排查

问题描述 家里的路由器使用拨号上网&#xff0c;路由器DHCP分发IP的范围是192.168.1.0/24。默认使用192.168.1.1管理路由器。然后拨号上网成功后&#xff0c;修改了私网IP的分发范围&#xff1a;192.168.5.1-192.168.5.10。为了防止有人蹭网&#xff0c;只分配的10个IP地址。修…

html5基础入门

html5基础语法与标签 前言前端开发零基础入门介绍前端开发行业介绍&#xff1a;大前端时代&#xff1a;前端开发主要技术介绍学习方法IDE简介vscode快捷键&#xff1a; 总结 HTML语法与基础标签互联网基本原理HTTP协议&#xff08;请求、响应&#xff09;什么是前端、后端&…

MongoDB认证考试小题库

Free MongoDB C100DBA Exam Actual Questions 关于MongoDB C100 DBA 考试真题知识点零散整理 分片架构 应用程序 --> mongos --> 多个mongod对于应用来说&#xff0c;连接分片集群跟连接一台单机mongod服务器一样分片好处&#xff0c; 增加可用RAM、增加可用磁盘空间、…

C++ 实现游戏(例如MC)键位显示

效果&#xff1a; 是不是有那味儿了&#xff1f; 显示AWSD&#xff0c;空格&#xff0c;Shift和左右键的按键情况以及左右键的CPS。 彩虹色轮廓&#xff0c;黑白填充。具有任务栏图标&#xff0c;可以随时关闭字体是Minecraft AE Pixel&#xff0c;如果你没有装&#xff08;大…

网络安全技术新手入门:利用永恒之蓝获取靶机控制权限

目录 前言 一、搜索永恒之蓝可用模块 二、使用攻击模块 三、配置攻击模块 四、攻击 五、总结 前言 相关法律声明&#xff1a;《中华人民共和国网络安全法》第二十七条 任何个人和组织不得从事非法侵入他人网络、干扰他人网络正常功能、窃取网络数据等危害网络安全的活动&…