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

代理模式

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

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

根据创建代理对象的方式和时机,代理模式可以被分为静态代理、动态代理等类型。其中,静态代理是由程序员在编译时期就定义好的代理类,而动态代理则是在程序运行时期通过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 为止


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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Java SE语法篇】8.面向对象三大特征——封装、继承和多态

📚博客主页:爱敲代码的小杨. ✨专栏:《Java SE语法》 ❤️感谢大家点赞👍🏻收藏⭐评论✍🏻,您的三连就是我持续更新的动力❤️ 文章目录 1. 封装1.1 封装的概念1.2 为什么封装1.3 封装的实现…

【c/python】用GTK实现一个带菜单的窗口

一、用python 在GTK中创建一个带菜单的窗口,可以通过使用Gtk.MenuBar、Gtk.Menu和Gtk.MenuItem组件来构建菜单。以下是一个基本的例子,展示了如何使用Python的PyGObject库创建一个简单的带菜单栏的GTK窗口。 import gi gi.require_version(Gtk, 3.0) f…

androidkiller的两种异常情况

第一种反编译时异常: Exception in thread “main” org.jf.dexlib2.dexbacked.DexBackedDexFile$NotADexFile: Not a valid dex magic value: cf 77 4c c7 9b 21 01 修改方法: 编辑 AndroidKiller 的 bin/apktool 目录下有一个 apktool.bat 文件 修改成…

SpringCloud:微服务

文章目录 微服务服务架构演变单例架构(集中式架构)分布式架构 微服务SpringCloud 微服务 服务架构演变 单例架构(集中式架构) 单例架构: 将业务的所有功能集中在一个项目中开发,打成一个包部署 优点&…

RK3568笔记八: Display子系统

modetest 是由 libdrm 提供的测试程序,可以查询显示设备的特性,进行基本的显示测试,以及设置显示的模式。 我们可以借助该工具来学习 Linux DRM 应用编程,另外为了深入分析 Rockchip DRM driver,有必要先了解一下这个…

设置了uni.chooseLocation,小程序中打不开

设置了uni.chooseLocation,在小程序打不开,点击没反应,地图显现不出来; 解决方案: 1.Hbuilder——微信开发者工具路径没有配置 打开工具——>设置 2.微信小程序服务端口没有开 解决方法:打开微信开发…

宝塔面板使用phpMyAdmin 502 Bad Gateway

第一步软件商店安装PHP 第二步设置phpMyAdmin,选择PHP版本 – 解决

浅谈电动机监控系统在企业降碳过程中的作用 ——安科瑞 顾烊宇

1.前言 据《2017-2022年中国电力工业产业专项调查及十三五市场商机分析报告》显示,从我国目前全社会用电结构来看,工商业用户耗电量约占 80%,其中电机耗电约占工业用电的 75%,全国总耗电的 60%,是用户终端耗电占比较大…

系列六、Spring Security中的认证 授权 角色继承

一、Spring Security中的认证 & 授权 & 角色继承 1.1、概述 关于Spring Security中的授权,请参考【系列一、认证 & 授权】,这里不再赘述。 1.2、资源类 /*** Author : 一叶浮萍归大海* Date: 2024/1/11 20:58* Description: 测试资源*/ Re…

flutter 打包安卓apk 常用配置

打包之前需要先不配置不然会报错 Execution failed for task ‘:app:mergeReleaseResources’. APP目录下的build.gradleaaptOptions.cruncherEnabled falseaaptOptions.useNewCruncher false如图 配置targetSdkVersion 、minSdkVersion 在android/app/src目录下的build.…

java数据结构与算法:单链表 SinglyLinkedList

单链表 SinglyLinkedList 创建实现类并实现方法 package com.lhs;public class SinglyLinkedList<E> implements List<E>{// 头节点private Node<E> first;// 尾节点private Node<E> last;// 节点数量private int size;public static class Node<…

VMware workstation安装debian-12.1.0虚拟机并配置网络

VMware workstation安装debian-12.1.0虚拟机并配置网络 Debian 是一个完全自由的操作系统&#xff01;Debian 有一个由普罗大众组成的社区&#xff01;该文档适用于在VMware workstation平台安装debian-12.1.0虚拟机。 1.安装准备 1.1安装平台 Windows 11 1.2软件信息 软…

【时事篇-03】20240113棋盘麦粒的问题 (黑白棋盘填入不同色彩文字,2的次方、sum法)

作品展示&#xff1a; 背景需求 棋盘麦粒的每个棋盘格子里有多少麦粒呢&#xff1f;64格一共需要多少麦粒 用Python写一个答案吧 项目:棋盘麦粒The grain problem 2的次方 sum() 作者:阿夏 时间:2024年1月13日19:03在印度有一个古老的传说&#xff1a;舍罕王打算奖赏国际象棋…

强化学习应用(一):基于Q-learning的物流配送路径规划研究(提供Python代码)

一、Q-learning算法简介 Q-learning是一种强化学习算法&#xff0c;用于解决基于马尔可夫决策过程&#xff08;MDP&#xff09;的问题。它通过学习一个值函数来指导智能体在环境中做出决策&#xff0c;以最大化累积奖励。 Q-learning算法的核心思想是使用一个Q值函数来估计每…

大模型实战营Day4 XTuner大模型单卡低成本微调实战

FINETUNE简介 LLM下游应用中&#xff0c;增量预训练和指令跟随是经常会用到的两种微调模式 指令跟随微调 使用场景&#xff1a;让模型学会对话模板&#xff0c;根据人类指令进行对话 训练数据&#xff1a;高质量的对话&#xff0c;问答数据 为什么需要指令微调&#xff1f; 因为…

1222. 密码脱落(dp划分)

题目&#xff1a; 1222. 密码脱落 - AcWing题库 思路&#xff1a; 代码&#xff1a; #include<cstdio> #include<cstring> using namespace std; const int N1010; int f[N][N];//表示以L和R为两端点的字符串的“最长”回文序列长度 char s[N];//存储输入的字符串…

java每日一题——ATM系统编写(答案及编程思路)

前言&#xff1a; 基础语句学完&#xff0c;也可以编写一些像样的程序了&#xff0c;现在要做的是多加练习&#xff0c;巩固下知识点&#xff0c;打好基础&#xff0c;daydayup! 题目&#xff1a;模仿银行ATM系统&#xff0c;可以创建用户&#xff0c;存钱&#xff0c;转账&…

探索商超货架场景目标检测性能,基于YOLOv8【n/s/m/l/x】全系列参数模型开发构建商超货架场景下亨氏米粉食品种类检测识别系统

在前面的系列博文中&#xff0c;我们陆续应用实践开发了很多有趣的项目&#xff0c;但是在密集排布场景下如商超购物场所内货架上货物种类目标检测模型的开发我们则少有涉及&#xff0c;正值周末&#xff0c;本文的主要目的就是想要实践构建这一场景下的目标检测模型&#xff0…

QSpace:Mac上的简洁高效多窗格文件管理器

在Mac用户中&#xff0c;寻找一款能够提升文件管理效率的工具是常见的需求。QSpace&#xff0c;一款专为Mac设计的文件管理器&#xff0c;以其简洁的界面、高效的多窗格布局和丰富的功能&#xff0c;为用户提供了一个全新的文件管理体验。 QSpace&#xff1a;灵活与功能丰富的结…

MySQL面试题 | 05.精选MySQL面试题

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…