Spring基础之AOP和代理模式

文章目录

    • 理解AOP
      • AOP的实现原理
    • AOP代理模式
      • 静态代理
      • 动态代理
        • 1-JDK动态代理
        • 2-CGLIB动态代理
    • 总结

理解AOP

OOP - - Object Oriented Programming 面向对象编程
AOP - - Aspect Oriented Programming 面向切面编程
AOP是Spring提供的关键特性之一。AOP即面向切面编程,是OOP编程的有效补充。使用AOP技术,可以将一些系统性相关的编程工作,独立提取出来,独立实现,然后通过切面切入进系统。从而避免了在业务逻辑的代码中混入了很多的系统性相关的逻辑–比如:事务管理、日志记录、权限管理等,从而达到了将不同的关注点分离出来松耦合的效果。

AOP的实现原理

AOP的实现原理就是代理模式----其关键点是AOP框架自动创建的AOP代理
AOP代理分为静态AOP代理和动态AOP代理:

  1. 静态AOP代理 –– 是指AspectJ实现的AOP代理,它是将切面代码直接编译到Java字节码文件中,发生在编译时。
  2. 动态AOP代理 –– 是指将切面代码进行动态织入实现的AOP代理,发生在运行时。Spring的AOP即为动态AOP实现技术为:JDK提供的动态代理技术和CGLIB(Code Generation Library动态字节码增强技术)。尽管实现技术不一样,但都是基于代理模式,都是生成一个代理对象。

AOP代理模式

代理模式的核心作用就是通过代理,控制对被代理的目标对象的访问。它的设计思路是:定义一个抽象角色{接口},让代理角色和真实角色分别去实现它。

真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。它只关注真正的业务逻辑。

代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并在前后可以附加自己的操作。
代理模式实现歌星演出场景

静态代理

要求被代理类和代理类同时实现相应的一套接口,通过代理类调用重写接口的方法,实际上调用的是原始对象的同样的方法。如下图

在这里插入图片描述

/**
 * 明星接口
 */
public interface Star {
    void sing();
}
/**
 * 真实明星类
 */
public class RealStar implements Star{
    @Override
    public void sing() {
        System.out.println("明星本人开始唱歌...");
    }
}

/**
 * 明星静态代理类
 */
public class ProxyStar implements Star{
    private Star star; //接收明星对象

    /**
     * 通过构造方法传进来真实的明星对象
     * @param star
     */
    public ProxyStar(Star star) {
        this.star = star;
    }

    @Override
    public void sing() {
        System.out.println("明星代理先进行商务谈判...");
        //真实明星唱歌
        star.sing();
        System.out.println("明星演出完,代理收款...");
    }
}

这样的话,逻辑就非常清晰了。在代理类中,可以看到,维护了一个Star对象,通过构造方法传进来一个真实的Star对象给其赋值,
然后在唱歌这个方法里,使用真实对象来唱歌。所以说面谈、收钱都是由代理对象来实现的,唱歌是代理对象让真实对象来做。

/**
 * 测试客户端
 */
public class TestClient {
    /**
     * 测试静态代理结果
     * @param args
     */
    public static void main(String[] args) {
        Star realStar = new RealStar(); //创建真实对象
        Star proxy = new ProxyStar(realStar); //创建代理对象
        proxy.sing();

    }
}

静态代理的优缺点:
优点:
  1.被代理的业务类只需要做好自己的业务,实现了责任分离,保证了业务类的重用性
  2.将业务类隐藏起来,起到了保护作用
缺点:
  1.代理对象的某个接口只服务于某一个业务对象,每个真实对象都得创建一个代理对象
  2.如果接口增加一个方法,除了所有实现类需要实现这个方法外,代理类也需要实现,增加了代码的复杂度和成本

动态代理

在程序运行时由JVM通过反射机制动态的创建出代理对象的字节码。代理对象和真实对象的关系是在程序运行时才确定

1-JDK动态代理

JDK动态代理是使用java.lang.reflect包中的Proxy类与InvocationHandler接口来完成的,要使用JDK动态代理,必须要定义接口

API分析:
(1) java.lang.reflect.Proxy 类
  |-Java动态代理机制生成的所有动态代理类的父类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。
   public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler hanlder)
方法职责:为指定类加载器、一组接口及调用处理器  生成动态代理类实例 
参数:
   loader        :类加载器
   interfaces        :目标(真实)对象实现的接口
   hanlder        :代理执行处理器
返回:动态生成的代理对象
(2) java.lang.reflect.InvocationHandler接口:
public Object invoke(Object proxy, Method method, Object[] args)
方法职责:负责集中处理动态代理类上的所有方法调用
参数: 
    proxy :  生成的代理对象
    method: 当前调用的真实方法对象
    args:    当前调用方法的实参
返回: 真实方法的返回结果
jdk动态代理操作步骤 
 实现InvocationHandler接口,创建自己增强代码的处理器。
② 给Proxy类提供ClassLoader对象和代理接口类型数组,创建动态代理对象。
③ 在处理器中实现增强操作。

示例:

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

/**
 * JDK动态代理处理类
 */
public class JdkProxyHandler implements InvocationHandler {
    /**
     * 用来接收真实明星对象
     */
    private Object realStar;

    /**
     * 传入真实明星对象,动态创建一个代理
     *
     * @param realStar
     * @return
     */
    public Object bind(Object realStar) {
        this.realStar = realStar;
        return Proxy.newProxyInstance(realStar.getClass().getClassLoader(),
                realStar.getClass().getInterfaces(), this);
    }


    /**
     * 当调用代理对象的方法时,会调用此方法
     *
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("明星代理先进行谈判...");
        Object result = method.invoke(realStar, args); //真实对象明星唱歌
        System.out.println("演出完,代理收款...");
        return result;
    }
}
/**
 * 测试Jdk动态代理客户端
 */
public class TestJdkProxyClient {
    public static void main(String[] args) {
        Star realStar = new RealStar(); //真实对象
        //创建一个代理对象实例
        Star proxy = (Star)new JdkProxyHandler().bind(realStar);
        //代理对象调用方法
        proxy.sing();
    }
}

相对于静态代理,JDK 动态代理大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度

2-CGLIB动态代理

由上面的分析可知,JDK 实现动态代理需要实现类通过接口定义业务方法,那对于没有接口的类,如何实现动态代理呢,这就需要 CGLIB 了。

  • CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类.
  • CGLIB动态代理是针对没有接口的类进行的代理,和JDK动态代理的区别是获取代理对象的方法不一样
/**
 * 真实明星类
 */
public class RealStar{
    public void sing() {
        System.out.println("明星本人开始唱歌...");
    }
}

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * Cglib代理处理类
 */
public class CglibProxyHandler implements MethodInterceptor {
    /**
     * 维护的目标对象
     */
    private Object realStar;
    /**
     *  Enhancer类是CGLIB中的一个字节码增强器,它可以方便的对你想要处理的类进行扩展
     */
    private Enhancer enhancer = new Enhancer();

    /**
     * 根据被代理对象的类型 ,动态创建一个代理对象
     * @param realStar
     * @return
     */
    public Object bind(Object realStar){
this.realStar = realStar;
        enhancer.setSuperclass(realStar.getClass()); //使用增强器,设置被代理对象为父类
        enhancer.setCallback(this);  //设置回调方法,拦截器
        //动态创建一个代理类对象,并返回
        return enhancer.create();
    }

    /**
     *  当调用代理对象的方法时,会调用此方法进行拦截
     * @param proxy 代理对象
     * @param method 需要增强的方法
     * @param objects 需要增强方法的参数
     * @param methodProxy  需要增强的方法的代理
     * @return 方法执行后的返回值
     * @throws Throwable
     */
    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("代理明星先进行演出谈判...");
        //invokeSuper方法调用的对象已经是增强了
        Object result = methodProxy.invokeSuper(object,args); //明星唱歌
        System.out.println("演出完成,代理去收款...");
        return result;
    }
}

/**
 * 测试Cglib动态代理客户端
 */
public class TestCglibProxyClient {
    public static void main(String[] args) {
        RealStar realStar = new RealStar(); //真实对象
        //创建一个代理对象实例
        RealStar proxy = (RealStar) new CglibProxyHandler().bind(realStar);
        //代理对象调用方法
        proxy.sing();
    }
}

关于CGLIB 动态代理总结:

  • CGLib所创建的动态代理对象在实际运行时候的性能要比JDK动态代理高不少,但是CGLIB 创建代理对象时所花费的时间却比JDK多得多,所以对于单例singleton的对象,因为无需频繁创建对象,用 CGLIB 合适,反之使用JDK方式要更为合适一些。
  • CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的,诸如private的方法也是不可以作为切面的。

小结
Spring AOP 中的代理使用逻辑:如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理实现 AOP;如果目标对象没有实现了接口,则采用 CGLIB 库,Spring 会自动在 JDK 动态代理和 CGLIB 动态代理之间转换

总结

  • AOP面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
  • 使用AOP技术,可以将一些系统性相关的编程工作,独立提取出来,独立实现,然后通过切面切入进系统。
  • Spring的AOP为动态AOP,实现的技术为: JDK提供的动态代理技术 和 CGLIB(动态字节码增强技术)
  • 代理模式--官方的定义为“为其他对象提供一种代理以控制对这个对象的访问”
  • java中有静态代理、JDK动态代理、CGLib动态代理的方式。静态代理指的是代理类是在编译期就存在的,相反动态代理则是在程序运行期动态生成的

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

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

相关文章

Java+SpringBoot,打造极致申报体验

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

Vivado MIG ip核使用教程

Step 1 在ip catalog中搜索mig ip核并打开&#xff0c;检查硬件配置 Step 2 Step 3 选择对其他芯片类型的兼容性&#xff0c;若无此方面需求&#xff0c;可直接点击next Step 4 选择存储器类型 Step 5 配置DDR3芯片工作频率、用户时钟、mig ip核输入时钟、DDR3芯片类型…

中兴通讯携吉林移动迈向5G-A新阶段,完成3CC技术应用

日前&#xff0c;中兴通讯携手中国移动吉林移动分公司&#xff0c;在5G-A领域取得新突破。具体来说&#xff0c;双方基于MTK芯片M80终端&#xff0c;完成了5G-A三载波聚合试点&#xff0c;实测下行速率达到理论峰值4.25Gbps&#xff0c;相比2.6G单载波速率提升2.5倍。如此成绩&…

目标检测新SOTA:YOLOv9 问世,新架构让传统卷积重焕生机

在目标检测领域&#xff0c;YOLOv9 实现了一代更比一代强&#xff0c;利用新架构和方法让传统卷积在参数利用率方面胜过了深度卷积。 继 2023 年 1 月 YOLOv8 正式发布一年多以后&#xff0c;YOLOv9 终于来了&#xff01; 我们知道&#xff0c;YOLO 是一种基于图像全局信息进行…

如何查看电脑使用记录?保障个人隐私和安全

查看电脑使用记录是了解电脑活动的一种重要方式&#xff0c;可以帮助用户追踪应用程序的使用情况、登录和关机时间、文件的访问记录等。在本文中&#xff0c;我们将介绍如何查看电脑使用记录的三个方法&#xff0c;以分步骤详细说明如何查看电脑使用记录&#xff0c;帮助用户更…

Java知识点一

hello&#xff0c;大家好&#xff01;我们今天开启Java语言的学习之路&#xff0c;与C语言的学习内容有些许异同&#xff0c;今天我们来简单了解一下Java的基础知识。 一、数据类型 分两种&#xff1a;基本数据类型 引用数据类型 &#xff08;1&#xff09;整型 八种基本数…

【Spring MVC篇】简单案例分析

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【Spring MVC】 本专栏旨在分享学习Spring MVC的一点学习心得&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 一、加法计算器二…

【苍穹外卖】一些开发总结

1、DTO、VO的区别 DTO:如果前端返回的实体类和对应的实体类比较较大差别 使用DTO来封装数据 后面在使用 BeanUtils.copyProperties() 将熟悉复制到对应的实体类中 VO:主要用于展示数据,例如在控制器层和视图层之间。它通常包含一些与显示相关的属性,如标题、描述等。 2…

Fastjson【RCE1.2.47】漏洞复现

★★免责声明★★ 文章中涉及的程序(方法)可能带有攻击性&#xff0c;仅供安全研究与学习之用&#xff0c;读者将信息做其他用途&#xff0c;由Ta承担全部法律及连带责任&#xff0c;文章作者不承担任何法律及连带责任。 1、Fastjson介绍 fastjson是阿里巴巴的开源JSON解析库&…

STM32_IIC_AT24C02_1_芯片简介即管脚配置

STM32的IIC总线是存在bug&#xff0c;感兴趣的可以上网搜一搜。我们可以使用两个I/O口和软件的方式来模拟stm32的iic总线的控制&#xff0c;所以就不需要使用stm32的硬件控制器了&#xff0c;同理数据手册中的I2C库函数也没有用了。 ROM&#xff08;只读存储器&#xff09;和…

普中51单片机学习(EEPROM)

EEPROM IIC串行总线的组成及工作原理 I2C总线的数据传送 数据位的有效性规定 I2C总线进行数据传送时&#xff0c;时钟信号为高电平期间&#xff0c;数据线上的数据必须保持稳定&#xff0c;只有在时钟线上的信号为低电平期间&#xff0c;数据线上的高电平或低电平状态才允许…

微服务-微服务Spring Security6实战

1. Spring Security介绍 1.1 Spring Security定义 Spring Security 是一个能够为基于 Spring 的企业应用系统提供声明式的安全访问控制解决方案的安全框 架。 Spring Security 主要实现了 Authentication &#xff08;认证&#xff0c;解决 who are you? &#xff09; 和…

数字化转型导师坚鹏:政府数字化流程管理

政府数字化流程管理 课程背景&#xff1a; 很多政府存在以下问题&#xff1a; 不清楚数字化对流程有什么影响&#xff1f; 不知道政府业流程如何进行优化&#xff1f; 不知道政府业流程优化的具体案例&#xff1f; 课程特色&#xff1a; 有实战案例 有原创观点 …

小程序画布(二维地图线)

首先开始是想用小程序兼容openlayers的&#xff0c;但是了解到用不了&#xff0c;那就用画布来解决 实际效果如下 wxml中代码 <canvas id"trackDesignCanvas" //指定 id 的 Canvas 组件class"orbit-canvas-main" type"2d" …

鸿蒙LiteOS-M 内核初始化

目录 一、LiteOS-M 初始化内核二、LOS_KernelInit代码分析三、LOS_Start代码解析坚持就有收获 一、LiteOS-M 初始化内核 在LiteOS-M应用程序中&#xff0c;系统初始化如下&#xff1a; /*** brief This is the ohos entry, and you could call this in your main funciton af…

Android 内存优化内存泄漏处理

一:匿名内部类/非静态内部类 匿名内部类的泄漏原因&#xff1a;匿名内部类会隐式地持有外部类的引用.当外部类被销毁时&#xff0c;内部类并不会自动销毁&#xff0c;因为内部类并不是外部类的成员变量&#xff0c; 它们只是在外部类的作用域内创建的对象&#xff0c;所以内部…

LabVIEW变压器振动信号数据采集与分析

LabVIEW变压器振动信号数据采集与分析 随着电力系统的快速发展&#xff0c;对变压器的安全监控和故障诊断需求日益增加。设计了一套基于LabVIEW的变压器振动信号数据采集与分析系统&#xff0c;提高变压器的运行安全性和可靠性&#xff0c;实现对变压器振动信号的实时监测和故…

c语言---数组(超级详细)

数组 一.数组的概念二. 一维数组的创建和初始化2.1数组的创建2.2数组的初始化错误的初始化 2.3 数组的类型 三. 一维数组的使用3.1数组的下标3.2数组元素的打印3.2数组元素的输入 四. 一维数组在内存中的存储五. 二维数组的创建5.1二维数组的概念5.2如何创建二维数组 六.二维数…

Flutter Engine 编译

本地环境 Flutter 开发基本环境配置&#xff0c;SDK【】 MAC. M2芯片 git工具 python环境[MAC自带] xcode Chromium depot_tools depot_tools 是调试 Flutter 引擎的必备工具包&#xff0c;包含了 gclient、gn 和 ninja 等工具&#xff0c;这些在下面会用到&#xff01;…

信号系统之线性图像处理

1 卷积 图像卷积的工作原理与一维卷积相同。例如&#xff0c;图像可以被视为脉冲的总和&#xff0c;即缩放和移位的delta函数。同样&#xff0c;线性系统的特征在于它们如何响应脉冲。也就是说&#xff0c;通过它们的脉冲响应。系统的输出图像等于输入图像与系统脉冲响应的卷积…
最新文章