Spring之AOP

news2024/11/24 17:55:08

谈起AOP就不得不说起代理,Java 源代码经过编译生成字节码,然后再由 JVM 经过类加载,连接,初始化成 Java 类型,可以看到字节码是关键,静态和动态的区别就在于字节码生成的时机

静态代理:由程序员创建代理类或特定工具自动生成源代码再对其编译。在编译时已经将接口,被代理类(委托类),代理类等确定下来,在程序运行前代理类的.class文件就已经存在了

动态代理:在程序运行后通过反射创建生成字节码再由 JVM 加载而成

AOP通常叫面向切面编程(Aspect-oriented Programming,简称AOP),它是一种编程范式,通过预编译的方式和运行期动态代理实现程序功能的统一维护的一种技术,通常用来对隔离不同业务逻辑,比如常见的事务管理、日志管理等

AOP核心概念

  • 切面(Aspect):似于 Java 中的类声明,常用于应用中配置事务或者日志管理。一般使用 @Aspect 注解或者  来定义一个切面
  • 连接点(Join Point):程序执行中的特定点,比如方法执行、处理一个异常等
  • 切点(Pointcut):通过一种规则匹配的正则表达式,当有连接点可以匹配到切点时,就会触发改切点相关联的指定通知
  • 通知(Advice):在切面中某个连接点采取的动作,通知方式也有5种
    • around(环绕通知):前后都加
    • before(前置通知)
    • after(后置通知)
    • exception(异常通知)
    • return(返回通知)
  • 织入(Weaving):链接切面和目标对象创建一个通知对象的过程

静态代理

静态代理的实现代码在这里做不做赘述,简单来讲就是委托类和代理类实现同一个接口,代理类用于委托类的增强

  • 静态代理主要有两大劣势
    • 代理类只代理一个委托类(其实可以代理多个,但不符合单一职责原则),也就意味着如果要代理多个委托类,就要写多个代理(别忘了静态代理在编译前必须确定)
    • 第一点还不是致命的,再考虑这样一种场景:如果每个委托类的每个方法都要被织入同样的逻辑,比如说我要计算前文提到的每个委托类每个方法的耗时,就要在方法开始前,开始后分别织入计算时间的代码,那就算用代理类,它的方法也有无数这种重复的计算时间的代码

动态代理

 JDK代理

由于动态代理是程序运行后才生成的,哪个委托类需要被代理到,只要生成动态代理即可,避免了静态代理那样的硬编码,另外所有委托类实现接口的方法都会在 Proxy 的 InvocationHandler.invoke() 中执行,这样如果要统计所有方法执行时间这样相同的逻辑,可以统一在 InvocationHandler 里写, 也就避免了静态代理那样需要在所有的方法中插入同样代码的问题,代码的可维护性极大的提高了

原理:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);
  1. loader:代理类的ClassLoader,最终读取动态生成的字节码,并转成 java.lang.Class 类的一个实例(即类),通过此实例的 newInstance() 方法就可以创建出代理的对象
  2. interfaces: 委托类实现的接口,JDK 动态代理要实现所有的委托类的接口
  3. InvocationHandler:委托对象所有接口方法调用都会转发到 InvocationHandler.invoke(),在 invoke() 方法里我们可以加入任何需要增强的逻辑 主要是根据委托类的接口等通过反射生成的

实现:

public class RealSubject implements Subject{
    @Override
    public void request() {
        System.out.println("卖房");
    }
}
public class ProxyFactory {

    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    public Object getProxyInstance() {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("开始");
                method.invoke(target, args);
                System.out.println("结束");
                return proxy;
            }
        });
    }

    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        System.out.println(realSubject.getClass());

        Subject subject = (Subject) new ProxyFactory(realSubject).getProxyInstance();
        System.out.println(subject.getClass());
        subject.request();
    }
}

结果输出:

代理类的 class 为 com.sun.proxy.$Proxy0,Proxy 是在 java.lang.reflect 反射包下的,Proxy 的 newProxyInstance 签名

由于动态代理是程序运行后才生成的,哪个委托类需要被代理到,只要生成动态代理即可,避免了静态代理那样的硬编码,另外所有委托类实现接口的方法都会在 Proxy 的 InvocationHandler.invoke() 中执行,这样如果要统计所有方法执行时间这样相同的逻辑,可以统一在 InvocationHandler 里写, 也就避免了静态代理那样需要在所有的方法中插入同样代码的问题,代码的可维护性极大的提高了

 CGLIB代理

既然JDK动态代理听起来没问题为什么Spring AOP要使用CGLIB 动态代理呢?

JDK 动态代理虽好,但也有弱点,我们注意到 newProxyInstance 的方法签名,注意第二个参数 Interfaces 是委托类的接口,是必传的, JDK 动态代理是通过与委托类实现同样的接口,然后在实现的接口方法里进行增强来实现的,这就意味着如果要用 JDK 代理,委托类必须实现接口,这样的实现方式看起来有点蠢,更好的方式是什么呢,直接继承自委托类不就行了(superClass),这样委托类的逻辑不需要做任何改动,CGlib 就是这么做的

原理:

开头我们提到的 AOP 就是用的 CGLib 的形式来生成的,JDK 动态代理使用 Proxy 来创建代理类,增强逻辑写在 InvocationHandler.invoke() 里,CGlib 动态代理也提供了类似的  Enhance 类,增强逻辑写在 MethodInterceptor.intercept() 中,也就是说所有委托类的非 final 方法都会被方法拦截器拦截

实现:

public class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("目标类增强前");
        Object object = methodProxy.invokeSuper(o, objects);
        System.out.println("目标增强后");
        return object;
    }

    public static void main(String[] args) {
        //创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数
        Enhancer enhancer = new Enhancer();
        //设置目标类的字节码文件
        enhancer.setSuperclass(RealSubject.class);
        //设置回调函数
        enhancer.setCallback(new MyMethodInterceptor());
        //这里的creat方法就是正式创建代理类
        RealSubject proxyDog = (RealSubject) enhancer.create();
        System.out.println(proxyDog.getClass());
        //调用代理类的eat方法
        proxyDog.request();
    }
}

结果输出:

 

它并不要求委托类实现任何接口,而且 CGLIB 是高效的代码生成包,底层依靠 ASM(开源的 java 字节码编辑类库)操作字节码实现的,性能比 JDK 强,所以 Spring AOP 最终使用了 CGlib 来生成动态代理,只能代理委托类中任意的非 final 的方法,另外它是通过继承自委托类来生成代理的,所以如果委托类是 final 的,就无法被代理了(final 类不能被继承)

总结:

JDK 动态代理的拦截对象是通过反射的机制来调用被拦截方法的

CGlib动态代理通过什么机制来提升了方法的调用效率:由于反射的效率比较低,所以 CGlib 采用了FastClass 的机制来实现对被拦截方法的调用。FastClass 机制就是对一个类的方法建立索引,通过索引来直接调用相应的方法

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

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

相关文章

BLE MESH中的Secure Network beacon包

作用&#xff1a;节点使用安全网络信标来识别子网及其安全状态。可以用来更新Key和Iv Index。 数据包结构&#xff1a; 数据包格式&#xff1a; 大小含义 Beacon Type 1安全网络信标&#xff08;0x01&#xff09; Flags1包含密钥刷新标志和IV更新标志 Network ID8包含网络ID的值…

代码随想录算法训练营第四天 | 24. 两两交换链表中的节点 19.删除链表的倒数第N个节点 面试题 02.07. 链表相交 142.环形链表II

今天是链表章节最后一天&#xff0c;加油&#x1f4aa; 24. 两两交换链表中的节点 题目&#xff1a;给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节…

html练习11:案例仿制

1.目标效果 2.布局效果 3.顶端部分制作效果 问题&#xff1a;img和p无法同时垂直居中显示&#xff0c;img会顶端对齐&#xff0c;p会底部对齐 解决方法&#xff1a;把img作为背景加入&#xff1b;用两个div分别做img和p的容器再进行格式调整 4.导航栏部分制作效果 要点&#…

保证项目如期上线,测试人能做些什么?

要保证项目按照正常进度发布&#xff0c;需要整个研发团队齐心协力。 有很多原因都可能会造成项目延期。 1、产品经理频繁修改需求 2、开发团队存在技术难题 3、测试团队测不完 今天我想跟大家聊一下&#xff0c;测试团队如何保证项目按期上线&#xff0c;以及在这个过程中可能…

词法分析程序

一、实验原理 1.1实验内容 通过本实验&#xff0c;应达到以下目标&#xff1a; 1.掌握从源程序文件中读取有效字符的方法和产生源程序的内部表示文件的方法。 2.掌握词法分析的实现方法。 3.上机调试编出的词法分析程序。 1.2实验内容 词法分析是作为相对独立的阶段来完成的…

C# 事件

一 C#中的事件 大致上&#xff1a;事件-----回调函数&#xff1b; 二 用户界面中的事件 ① 按钮点击事件 ② 基本的写法 this.button1.Clicknew System.EventHandler(this.button1_Click); private void button1_Click(object sender,EventHandler e) {this.label1.TextDat…

C++智能指针weak_ptr

C智能指针weak_ptr 学习路线&#xff1a;C智能指针shared_ptr->C智能指针unique_ptr->C智能指针weak_ptr 简介&#xff1a;本文讲解常用的智能指针的用法和原理&#xff0c;包括shared_ptr,unique_ptr,weak_ptr。 概述 weak_ptr设计的目的是为配合 shared_ptr 而引入…

静电场方程与边界面上的衔接条件 工程电磁学 P6

我们现在已经知道两个公式 我们可以得到微分形式 对于体密度&#xff0c;面密度&#xff0c;线密度&#xff0c;点电荷的理解 很多同学会问空间中为什么要有面密度&#xff0c;线密度的存在呢&#xff1f; 其实这个是模型的需要&#xff0c;因为介质不一定是连续的&#xff0…

如何设计一个高性能的图 Schema

本文整理自青藤云安全工程师——文洲在青藤云技术团队内部分享&#xff0c;分享视频参考&#xff1a;https://www.bilibili.com/video/BV1r64y1R72i 图数据库的性能和 schema 的设计息息相关&#xff0c;但是 NebulaGraph 官方本身对图 schema 的设计其实没有一个定论&#xff…

Codeforces Round #837 (Div. 2) C. Hossam and Trainees

Problem - C - Codeforces 翻译&#xff1a; 胡萨姆有&#x1d45b;名学员。他给&#x1d456;-th的学员分配了一个号码&#x1d44e;&#x1d456;。 一双&#x1d456;-th和&#x1d457;-th(&#x1d456;≠&#x1d457;)学员被称为成功的如果有一个整数&#x1d465;(&…

基于springboot的企业员工工资管理系统(财务系统)

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下&#xff0c;你想解决的问…

Vue渲染器(二):挂载与更新

渲染器&#xff08;二&#xff09;&#xff1a;挂载与更新 前面介绍了渲染器的基本概念和整体框架&#xff0c;接下来就可以介绍渲染器的核心功能&#xff1a;挂载与更新。 1.挂载子节点和元素的属性&#xff1a; vnode.children的值为字符串类型时&#xff0c;会把它设置为…

019 | 在线电影娱乐网站系统设计含论文 | 大学生毕业设计 | 极致技术工厂

作为一个在线电影娱乐网站系统&#xff0c;它展示给浏览者的是各种电影信息&#xff0c;把这些信息能够按用户的需要友好的展示出来是很重要的&#xff0c;同时&#xff0c;能够实现对这些信息的有条不紊的管理也是不可以忽视的。对浏览者和会员的功能而言叫做前台实现&#xf…

[附源码]Node.js计算机毕业设计电子购物商城Express

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…

[附源码]计算机毕业设计电商小程序Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; Springboot mybatis MavenVue等等组成&#xff0c;B/S模式…

【GRU时序预测】基于卷积神经网络结合门控循环单元CNN-GRU实现时间序列预测附matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

代码随想录训练营第49天|LeetCode 121. 买卖股票的最佳时机、122.买卖股票的最佳时机II

参考 代码随想录 题目一&#xff1a;LeetCode 121. 买卖股票的最佳时机 注意这个题只买卖一次&#xff01;&#xff01; 贪心 class Solution { public:int maxProfit(vector<int>& prices) {int low INT_MAX;int result 0;for(int i 0; i < prices.size(…

Redis框架(十):大众点评项目 订单功能 Redis实现全局唯一ID、 秒杀基本环境

大众点评项目 订单功能 秒杀基本环境需求&#xff1a;订单功能 秒杀基本环境Redis实现全局唯一ID业务实现代码总览总结SpringCloud章节复习已经过去&#xff0c;新的章节Redis开始了&#xff0c;这个章节中将会回顾Redis实战项目 大众点评 主要依照以下几个原则 基础实战的Dem…

揭秘!全球2022年Salesforce不同招聘职位的平均薪资

Salesforce可以说是发展最快的企业软件公司。此外&#xff0c;还一直被评选为全球最佳工作场所之一&#xff0c;2021年赢得了Glassdoor评选的最佳工作场所&#xff0c;并且在《财富》杂志的100家最佳工作公司中排名第四。除了非常重视员工福利&#xff0c;强调工作与生活的平衡…

在WSL中配置GPU环境

首先需要明确一点&#xff0c;虽然我们通过安装WSL获得了linux开发环境&#xff0c;但是我们最终使用的GPU还是在windows当中的&#xff0c;所以还是需要在系统中安装对应的驱动。 第一步&#xff1a;在window上根据显卡型号和版本安装驱动 这里参考之前的步骤就行 第二步&a…