Spring原理学习(六):Spring实现动态代理时对jdk和cglib的选择

news2024/9/22 9:41:20

目录

〇、前言

一、AOP中的一些基本概念 

二、两个切面的概念

三、advisor的使用

3.1 前置知识 

3.2 使用步骤

四、spring对jdk和cglib的统一 


〇、前言

        对jdk和cglib 实现动态代理的原理不清楚的兄弟们,可以参考前文:Spring原理学习(五):一篇讲清楚动态代理(jdk和cglib)的使用、原理和源码_玉面大蛟龙的博客-CSDN博客

        spring当中不需要我们直接去用jdk或者cglib,它提供了ProxyFactory来方便地创建代理,那么他如何选择代理方法呢?我打算通过AOP中的切面来讲解这一部分。 

一、AOP中的一些基本概念 

        复习一下AOP基本的概念,对AOP不熟悉的可以参考我的博客:Spring5学习(七):注解方式进行AOP操作 及 多种通知类型的测试_玉面大蛟龙的博客 

        切点相当于匹配规则。并不是所有方法都要增强,符合切点的方法才会增强。

        通知就是增强的逻辑。

        切面 = 切点 + 通知。

二、两个切面的概念

  • aspect:可以包含多个切面
aspect = 
    通知1(advice) + 切点1(pointcut)
    通知2(advice) + 切点2(pointcut)
    通知3(advice) + 切点3(pointcut)
    ...
  • advisor:更细粒度的切面。包含一个通知和一个切点

        aspect在底层逻辑中也是拆分为多个advisor进行处理的,所以我们就使用更细粒度的advisor吧。

三、advisor的使用

3.1 前置知识 

        这是Adivsor的简要的类图。Adivsor是由切点(Pointcut)和Advice(通知)组成,Pointcut 和 Advice都有极其丰富的实现,我们选熟悉的来使用。

        下图是PointCut的实现,我们选择 AspectJExpressionPointcut (根据AspectJ表达式的切点)来实现。

        下图是Advice的实现,我们选取 MethodInterceptor 来实现。本质上它是一种环绕通知。

        注意,这里的 MethodInterceptor 是 spring使用的 MethodInterceptor,包名为org.aopalliance.intercept.MethodInterceptor,跟我们前面介绍的cglib中的不是一回事,只是同名而已。 

 

3.2 使用步骤

        使用切面的步骤如下:

  1. 备好切点
  2. 备好通知
  3. 备好切面
  4. 创建代理
public class A15 {
    public static void main(String[] args) {
        /*
            两个切面概念
            aspect =
                通知1(advice) +  切点1(pointcut)
                通知2(advice) +  切点2(pointcut)
                通知3(advice) +  切点3(pointcut)
                ...
            advisor = 更细粒度的切面,包含一个通知和切点
         */

        // 1. 备好切点
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        // 匹配所有类中的foo方法,因此Target中的bar方法不会被匹配到
        pointcut.setExpression("execution(* foo())");

        // 2. 备好通知
        MethodInterceptor advice = invocation -> {
            // 前置增强
            System.out.println("before...");
            // 调用目标
            Object result = invocation.proceed();
            // 后置增强
            System.out.println("after...");
            return result;
        };

        // 3. 备好切面
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);

        // 4. 创建代理
        Target1 target = new Target1();
        // 这里不需要我们直接去用jdk或者cglib,spring提供了ProxyFactory来方便地创建代理
        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(target);
        factory.addAdvisor(advisor);
        I1 proxy = (I1) factory.getProxy();
        System.out.println(proxy.getClass());
        proxy.foo();
        proxy.bar();
    }

    interface I1 {
        void foo();

        void bar();
    }

    /**
     * Target1实现了接口
     */
    static class Target1 implements I1 {
        public void foo() {
            System.out.println("target1 foo");
        }

        public void bar() {
            System.out.println("target1 bar");
        }
    }

    /**
     * Target2没有实现接口
     */
    static class Target2 {
        public void foo() {
            System.out.println("target2 foo");
        }

        public void bar() {
            System.out.println("target2 bar");
        }
    }
}

        运行后发现,使用的代理方式是cglib。那么,spring是如何选择代理方式的呢? 

 

四、spring对jdk和cglib的统一 

         先说结论:

  • proxyTargetClass = false, 目标实现了接口, 用 jdk 实现
  • proxyTargetClass = false, 目标没有实现接口, 用 cglib 实现
  • proxyTargetClass = true, 总是使用 cglib 实现

        介绍一下 ProxyTargetClass:它是ProxyFactory的父类的一个属性:

        proxyTargetClass属性其实就是是用来配置是否代理目标类。简而言之就是是否所有的代理对象都通过 CGLIB 的方式来创建,在之后的流程中,Spring 会根据 Bean 实例来判断是采用 JDK 动态代理的方式创建代理对象,还是通过 CGLIB 的方式创建代理对象,如果proxyTargetClass属性配置为true,则全部采用 CGLIB 的方式。

        演示结果: 

        1、 proxyTargetClass = false, 目标实现了接口, 用 jdk 实现

// 4. 创建代理
        Target1 target = new Target1();
        // 这里不需要我们直接去用jdk或者cglib,spring提供了ProxyFactory来方便地创建代理
        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(target);
        factory.addAdvisor(advisor);
        // 告诉工厂,这个方法上继承了什么接口
        factory.setInterfaces(target.getClass().getInterfaces());
        // 设置ProxyTargetClass参数
        factory.setProxyTargetClass(false);
        I1 proxy = (I1) factory.getProxy();
        System.out.println(proxy.getClass());
        proxy.foo();
        proxy.bar();

        2、 proxyTargetClass = false, 目标没有实现接口, 用 cglib 实现

// 4. 创建代理
        Target2 target = new Target2();
        // 这里不需要我们直接去用jdk或者cglib,spring提供了ProxyFactory来方便地创建代理
        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(target);
        factory.addAdvisor(advisor);
        // 告诉工厂,这个方法上继承了什么接口
        factory.setInterfaces(target.getClass().getInterfaces());
        // 设置ProxyTargetClass参数
        factory.setProxyTargetClass(false);
        Target2 proxy = (Target2) factory.getProxy();
        System.out.println(proxy.getClass());
        proxy.foo();
        proxy.bar();

        3、 proxyTargetClass = true, 总是使用 cglib 实现

        目标类继承了接口 

// 4. 创建代理
        Target1 target = new Target1();
        // 这里不需要我们直接去用jdk或者cglib,spring提供了ProxyFactory来方便地创建代理
        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(target);
        factory.addAdvisor(advisor);
        // 告诉工厂,这个方法上继承了什么接口
        factory.setInterfaces(target.getClass().getInterfaces());
        // 设置ProxyTargetClass参数
        factory.setProxyTargetClass(true);
        I1 proxy = (I1) factory.getProxy();
        System.out.println(proxy.getClass());
        proxy.foo();
        proxy.bar();

  

        目标类没继承接口:

// 4. 创建代理
        Target2 target = new Target2();
        // 这里不需要我们直接去用jdk或者cglib,spring提供了ProxyFactory来方便地创建代理
        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(target);
        factory.addAdvisor(advisor);
        // 告诉工厂,这个方法上继承了什么接口
        factory.setInterfaces(target.getClass().getInterfaces());
        // 设置ProxyTargetClass参数
        factory.setProxyTargetClass(true);
        Target2 proxy = (Target2) factory.getProxy();
        System.out.println(proxy.getClass());
        proxy.foo();
        proxy.bar();

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

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

相关文章

Python+Qt人脸识别职工录入管理系统

程序示例精选 PythonQt人脸识别职工录入管理系统 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对<<PythonQt人脸识别职工录入管理系统>>编写代码&#xff0c;代码整洁&#xff0c…

【FTP】——文件传输协议

文章目录 1.FTP简介1.1 FTP概述1.2 FTP主动模式1.3 FTP被动模式 2. 实例&#xff1a;匿名用户访问FTP服务3. 实例&#xff1a;本地用户访问FTP服务 1.FTP简介 1.1 FTP概述 FTP服务——用来传输文件的协议。 FTP服务器默认使用TCP协议的20、21端口与客户端进行通信. 20端口…

【学习笔记】Linux基础

Linux基础 一、操作系统1、什么是操作系统2、Linux操作系统3、Linux系统目录&#xff0c;Linux倒挂树型目录结构&#xff1a;4、安装Xshell与Xftp5、Linux文件操作命令6、vim文本编辑器&#xff08;1&#xff09;vim三种模式&#xff08;2&#xff09;vim重要快捷键&#xff08…

Dell Inspiron 5570电脑 Hackintosh 黑苹果efi引导文件

原文来源于黑果魏叔官网&#xff0c;转载需注明出处。&#xff08;下载请直接百度黑果魏叔&#xff09; 硬件型号驱动情况 主板Dell Inspiron 5570 处理器Intel(R) Core(TM) i7-8550U CPU 1.80GHz已驱动 内存8 GB 2400 MHz DDR4已驱动 硬盘samsung ssd 850 evo 250 go已驱…

汽车跨界还能这么玩?解锁汽车跨界新模式

跨界&#xff0c;是一种商业思维&#xff0c;是出圈方式&#xff0c;是进入流量红海的手段之一。近几年来&#xff0c;国内掀起了一股跨界风&#xff0c;“跨界增值宣传”似乎成为了品牌年轻化的必经之路&#xff0c;众多品牌通过跨界的方式实现互相补足、用户渗透&#xff0c;…

#Chrome扩展程序开发教程--01:基本概念介绍

#Chrome扩展程序开发教程--01&#xff1a;基本概念介绍 引言1、什么是扩展程序&#xff1f;2、Web技术3、Chrome 扩展程序API4、扩展程序架构 引言 本系列博客旨在带来最新的Chrome扩展程序开发入门教程。 1、什么是扩展程序&#xff1f; 通过向Chrome浏览器添加自定义特性和功…

Docker容器---网络、容器操作

Docker容器---网络、容器操作 一、docker实现原理二、docker网路模式1、Host模式2、container模式3、none模式4、bridge模式 三、自定义网络1、查看网络模式列表2、查看容器信息3、指定分配IP地址4、自定义网络固定IP 四、暴露端口五、容器端口映射1、创建端口映射 六、资源控制…

wordpress建站/demo

bidewang.co/ele 建站的另一种途径Sign in – My Account tanrenchang.atspace.cc 登录 ‹ Fashion trading platform — WordPress cPanel 是一个用于管理网站的虚拟主机控制面板。 使用 cPanel&#xff0c;可以轻松管理电子邮件帐户、数据库、FTP 用户以及与托管、设置和管…

windows10下使用minGW64 编译krita源码报错

系列文章目录 文章目录 系列文章目录前言一、错误原因二、使用步骤1.引入库 前言 collect2.exe: error: ld returned 1 exit status mingw32-make[2]: *** [plugins\color\lcms2engine\CMakeFiles\kritalcmsengine.dir\build.make:614: bin/kritalcmsengine.dll] Error 1 ming…

websever|2.19-2.27|信号概述-SIGCHILD信号

2.19信号概述 信号也是进程间通信的一种方式 其中1-31是操作系统定义的标准信号&#xff0c;比较重要。需要掌握其中几个。 34-64是预定义好的信号&#xff0c;是实时的信号 core文件中保存异常终止的一些信息。 在2.20节的开头&#xff0c;老师重点讲解了 的core文件。 进程出…

57 openEuler搭建Mariadb数据库服务器-管理数据库用户

文章目录 57 openEuler搭建Mariadb数据库服务器-管理数据库用户57.1 创建用户57.2 查看用户57.3 修改用户57.3.1 修改用户名57.3.2 修改用户示例57.3.3 修改用户密码57.3.4 修改用户密码示例 57.4 删除用户57.5 用户授权57.6 删除用户权限 57 openEuler搭建Mariadb数据库服务器…

集群聊天服务器项目(四)——项目总结

集群聊天服务器项目总结 首先是就是项目介绍集群聊天服务器项目(零)——项目介绍中的内容&#xff0c;就不再次copy过来了 项目简单介绍 技术栈 环境和库依赖 按模块介绍整个项目 程序的主要模块是网络模块、业务模块、数据模块、Json、redis发布订阅消息队列模块以及ngi…

Anaconda环境闭着眼睛安装tensorflow2.0-GPU

1.创建conda环境 conda create -n tf2 python3.7 2.进入conda环境 conda activate tf2 3.输入 nvidia-smi 查看有没有显卡驱动。(没有安一个&#xff0c;不管是windows/linux) 4. 安装cudatoolkit 和 cuDNN conda install cudatoolkit10.0 cudnn 5. 安装tensorflow pip ins…

【最佳实践】OAuth标准和基于OAuth2.0实现Github 授权单点登录的保姆级教程

【最佳实践】OAuth标准和基于OAuth2.0实现Github 授权单点登录的保姆级教程 第一章&#xff1a;OAuth基础知识1.1 OAuth起源1.2 OAuth简介1.3 OAuth的角色1.4 OAuth的授权流程1.5 OAuth的安全性1.6 OAuth标准的历史版本 第二章&#xff1a;OAuth2.0的工作原理2.1 OAuth2.0简介2…

前端--移动端布局--1移动web开发流式布局

目标&#xff1a; 能够知道移动web的开发现状 能够写出标准的viewport 能够使用移动web的调试方法 能够说出移动端常见的布局方案 能够描述流式布局 能够独立完成京东移动端首页 目录&#xff1a; 移动端基础 视口 二倍图 移动端调试 移动端技术解决方案 移动端常…

【全屏导航栏菜单】

提示&#xff1a;全屏导航栏菜单,炫酷的全局动画和导航切换动画 前言 提示&#xff1a;以下是本篇文章的代码内容,供大家参考,相互学习 一、html代码 <!DOCTYPE html> <html><head><meta http-equiv"content-type" content"text/html; c…

浅尝GoWeb开发之Gin框架

一、框架简介 gin 目前应用最广泛的golang框架&#xff0c;甚至已经变成了golang的官方框架&#xff0c;但它主要是一个RESTFul的框架。封装比较优雅&#xff0c;API友好&#xff0c;源码注释比较明确。个人比较推荐。 beego 国内最早的golang框架&#xff0c;也是最全的MV…

opencv (二十二) 创建滑动条

滑动条(Trackbar)是OpenCV动态调节参数特别好用的一种工具,它依附于窗口存在。 创建滑动条:createTrackbar()函数 createTrackbar函数用于创建一个可以调整数值的滑动条(也称轨迹条),并将滑动条附加到指定的窗口上,它往往会和一个回调函数配合起来使用。 int createT…

你的GPT跟ChatGPT可能只差了一个DPU

“人类永远不会嫌网络太快&#xff0c;就像永远不会嫌高铁太快&#xff0c;你只会嫌它慢&#xff0c;希望它更快些。” 一个月内&#xff0c;百度、阿里、腾讯、商汤、讯飞、360等国内大厂扎堆发布“中国版 GPT ”&#xff0c;这家的名字还没记清楚&#xff0c;另一家的又蹦了出…

python逝练系列(终章)

目录 1、(最大数的出现)编写程序读取整数,找出它们中的最大值&#xff0c;然后计算它的出现次数。假设输入以数字0结束。假设你输入的是“352555 0";程序找出的最大数是5&#xff0c;而5的出现次数是4。(提示:维护两个变量max和 count。变量max存储的是当前最大数&#xf…