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{@Overridepublic void sing() {System.out.println("明星本人开始唱歌...");}
}/*** 明星静态代理类*/
public class ProxyStar implements Star{private Star star; //接收明星对象/*** 通过构造方法传进来真实的明星对象* @param star*/public ProxyStar(Star star) {this.star = star;}@Overridepublic 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*/@Overridepublic 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*/@Overridepublic 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.rhkb.cn/news/262963.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

年关将至送大礼 社区适时献爱心

在这个快节奏的时代&#xff0c;社区作为人们生活的重要组成部分&#xff0c;其凝聚力和互助精神显得尤为重要。2024年2月7日&#xff0c;实践队员李若钰有幸参与了社区礼盒分装的活动&#xff0c;这不仅仅是一次简单的劳动&#xff0c;更是一次心灵的洗礼和感悟。 礼盒分装&am…

10.CSS3的calc函数

CSS3 的 calc 函数 经典真题 CSS 的计算属性知道吗&#xff1f; CSS3 中的 calc 函数 calc 是英文单词 calculate&#xff08;计算&#xff09;的缩写&#xff0c;是 CSS3 的一个新增的功能。 MDN 的解释为可以用在任何长度、数值、时间、角度、频率等处&#xff0c;语法如…

Git基本操作(2)

Git基本操作&#xff08;2&#xff09; 上交文件之后&#xff0c;git文件的变化git cat-file HEAD指针里面有啥文件被修改git statusgit diff 文件名 版本回退&#xff08;git reset&#xff09;撤销回退git reflog 撤销的三种情况还没有addgit checkout -- [file] 已经add还没…

week04day03(爬虫 beautifulsoup4、)

一. 使用bs4解析网页 下载bs4 - pip install beautifulsoup4 使用的时候 import bs4专门用于解析网页的第三方库 在使用bs4的时候往往会依赖另一个库lxml pip install lxml 网页代码 <!DOCTYPE html> <html><head><meta charset"utf-8"><…

js谐音梗创意小游戏《望子成龙》

&#x1f33b; 前言 龙年到来&#xff0c;祥瑞满天。愿您如龙般矫健&#xff0c;事业腾飞&#xff1b;如龙鳞闪耀&#xff0c;生活美满。祝您龙年大吉&#xff0c;万事如意&#xff01; 龙年伊始&#xff0c;我给各位设计了一款原创的小游戏&#xff0c;话不多说&#xff0c;直…

vue3前端项目开发,具备纯天然的防止爬虫采集的特征

vue3前端项目开发,具备纯天然的防止爬虫采集的特征&#xff01;众所周知&#xff0c;网络爬虫可以在网上爬取到一些数据&#xff0c;很多公司&#xff0c;为了自己公司的数据安全&#xff0c; 尤其是web端项目&#xff0c;不希望被爬虫采集。那么&#xff0c;您可以使用vue技术…

redis GEO 类型原理及命令详解

目录 前言 一、GeoHash 的编码方法 二、Redis 操作GEO类型 前言 我们有一个需求是用户搜索附近的店铺&#xff0c;就是所谓的位置信息服务&#xff08;Location-Based Service&#xff0c;LBS&#xff09;的应用。这样的相关服务我们每天都在接触&#xff0c;用滴滴打车&am…

离散数学(一) 集合

属于关系 表示 枚举法&#xff1b; 叙述法&#xff1b; 文氏图法 基数 空集 全集 全集是相对唯一的 相等关系 有相同元素看作一个元素 包含关系 幂集 集合运算 并集 交集 补集 差集 对称差集 定理 可数集合与不可数集合 自然数集 等势 如果存在集合A到集合B的双射(又称一一…

【Oracle】玩转Oracle数据库(三):数据库的创建和管理

前言 嘿&#xff0c;各位数据库小能手们&#xff01;今天我们要进入数据库的创世纪&#xff0c;探索Oracle数据库的创建和管理&#xff01;&#x1f527;&#x1f4bb; 在这篇博文【Oracle】玩转Oracle数据库&#xff08;三&#xff09;&#xff1a;数据库的创建和管理中&#…

Flutter常用命令,持续更新

目录 前言 Flutter 常用命令 Dart 常用命令 adb 常用命令&#xff08;用于 Android 开发&#xff09; 前言 当在开发Flutter项目时&#xff0c;熟悉一些常用的命令是非常重要的。这些命令可以帮助你执行各种任务&#xff0c;从构建应用程序到调试和测试。以下是一些Flutte…

Unity中URP实现水体效果(水的深度)

文章目录 前言一、搭建预备场景1、新建一个面片&#xff0c;使其倾斜一个角度&#xff0c;来模拟水底和岸边的效果2、随便创建几个物体&#xff0c;作为与水面接触的物体3、再新建一个面片&#xff0c;作为水面 二、开始编写水体的Shader效果1、新建一个URP基础Shader2、把水体…

Redis篇之Redis持久化的实现

持久化即把数据保存到可以永久保存的存储设备当中&#xff08;磁盘&#xff09;。因为Redis是基于内存存储数据的&#xff0c;一旦redis实例当即数据将会全部丢失&#xff0c;所以需要有某些机制将内存中的数据持久化到磁盘以备发生宕机时能够进行恢复&#xff0c;这一过程就称…

TreeData 数据查找

TreeData 数据查找 最近做需求的时候遇到了这样的一个需求&#xff0c;Tree组件数据支持查找&#xff0c;而且TreeData的数据层级是无限级的 开始想的事借助UI组件库&#xff08;Ant-design-vue&#xff09;中的Tree组件的相关方法直接实现,看了下api 发现没法实现&#xff0c;…

C#实用开发(14)--高清晰度字体和窗体分辨率问题。

新建winform程序是&#xff0c;又是会感觉到字体清晰度不够高。还有一种现象就是分辨率的问题&#xff0c;我们平常在自己的电脑开发是用125百分比的分辨率&#xff0c;实际部署的工控机是100&#xff0c;这就会导致分辨率不一致的问题。 可以通过新建应用程序清单&#xff0c;…

css复习

盒模型相关&#xff1a; border&#xff1a;1px solid red (没有顺序) 单元格的border会发生重叠&#xff0c;如果不想要重叠设置 border-collapse:collapse (表示相邻边框合并在一起) padding padding影响盒子大小的好处使用 margin应用&#xff1a; 行内或行内块元素水…

c编译器学习07:minilisp编译器改造(debug模式支持调试)

问题 原版的minilisp编译器不支持argv输入测试&#xff0c;不方便单步调试。 代码改造目标是既不改变原有程序的各种功能&#xff0c; 又能支持个人习惯的vs单步debug模式。 CMakeLists.txt变更 定义DEBUG宏 解决单步调试源码定位偏差问题 cmake_minimum_required(VERSION …

【Linux进阶之路】Socket —— “UDP“ “TCP“

文章目录 一、再识网络1. 端口号2. 网络字节序列3.TCP 与 UDP 二、套接字1.sockaddr结构2.UDP1.server端1.1 构造函数1.2 Init1.3 Run 2.客户端1.Linux2.Windows 3.TCP1. 基本接口2. 客户端3. 服务端1.版本12.版本23.版本34.版本4 三、守护进程尾序 温馨提示&#xff1a;文章较…

【区块链】智能交易模式下的数据安全流通模型

【区块链】智能交易模式下的数据安全流通模型 写在最前面**区块链智能交易模式概述****数据安全流通的挑战****数据安全流通模型的核心要素****实现数据安全流通的区块链技术****区块链智能交易模式下数据安全流通模型的设计原则****数据安全流通模型的应用案例分析****面临的挑…

金航标电子位于广西柳州鹿寨县天线生产基地于大年正月初九开工了

金航标电子位于广西柳州鹿寨县天线生产基地于大年正月初九开工了&#xff01;&#xff01;&#xff01;金航标kinghelm&#xff08;www.kinghelm.com.cn&#xff09;总部位于中国深圳市&#xff0c;兼顾技术、成本、管理、效率和可持续发展。东莞塘厦实验室全电波暗室、网络分析…

C++初阶:容器适配器priority_queue常用接口详解及模拟实现、仿函数介绍

介绍完了stack和queue的介绍以及模拟的相关内容后&#xff1a;C初阶&#xff1a;容器适配器介绍、stack和queue常用接口详解及模拟实现 接下来进行priority_queue的介绍以及模拟&#xff1a; 文章目录 1.priority_queue的介绍和使用1.1priority_queue的初步介绍1.2priority_que…