当前位置: 首页 > news >正文

SpringAOP实现的两种方式-JDK动态代理和CGLIB动态代理

前言

想要了解SpringAOP的实现方式,需要先了解什么是AOP

OOP和AOP的区别

  • OOP 面向对象,允许开发者定义纵向的关系,但并适用于定义横向的关系,导致了大量代码的重复,而不利于各个模块的重用。

  • AOP,一般称为面向切面,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证.日志.事务处理。

AOP的核心

        AOP 实现的关键在于 代理模式,AOP 代理主要分为静态代理动态代理。静态代理的代表为 AspectJ;动态代理则以 Spring AOP 为代表

 什么是动态代理?

        Spring AOP 使用的动态代理,所谓的动态代理就是说 AOP 框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个 AOP 对象,这个 AOP 对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。

Spring AOP 中的动态代理主要有两种方式,JDK 动态代理CGLIB 动态代理

 JDK 动态代理

        JDK 动态代理只提供接口的代理,不支持类的代理。核心 InvocationHandler 接口和 Proxy 类,InvocationHandler 通过 invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用 InvocationHandler 动态创建一个符合某一接口的的实例, 生成目标类的代理对象。

 CGLIB 动态代理

        如果代理类没有实现 InvocationHandler 接口,那么 Spring AOP 会选择使用 CGLIB 来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现 AOP。CGLIB 是通过继承的方式做的动态代理,因此如果某个类被标记为 final,那么它是无法使用 CGLIB 做动态代理的。

 JDK 动态代理和 CGLIB 动态代理区别 

  • Spring默认使用JDK动态代理实现AOP代理,主要用于代理接口
  • CGLIB代理,实现类的代理,而不是接口

也就是说,JDK的动态代理是对接口的代理,而CGLIB的代理是对类的代理

 实例分析

JDK动态代理

我们创建一个电冰箱接口,和它的实现类,观察使用JDK代理和CGLIB代理的区别

FridgeBiz

public interface FridgeBiz {/*** 购买电冰箱* @param num*/public void buy(int num,int stock);
}

FridgeBizImpl 

@Component(value = "fridgeBiz")
public class FridgeBizImpl implements FridgeBiz {private int stock;/*** 购买电冰箱** @param num*/@Overridepublic void buy(int num,int stock) {this.stock=stock;System.out.println("顾客购买了"+num+"台电冰箱");}
}

 创建Aspect切面类:

LogAspect

@Aspect
@Component
public class LogAspect {@Pointcut("execution(void com.csx.service.impl.FridgeBizImpl.buy(int,int))")public void pt(){}@Around("pt()")public Object around(ProceedingJoinPoint pjp) throws Throwable {String methodName=  pjp.getSignature().getName();//获取请求方法的参数Object[] args = pjp.getArgs();System.out.println("电冰箱大折扣!,每人限购一台");try {return pjp.proceed();} finally {if ((Integer)args[1]<=(Integer) args[0]){System.out.println("电冰箱数量为"+args[1]+",电冰箱数量不足,请订购");}if ((Integer) args[0]!=1){System.out.println("顾客购买台数为"+args[0]+",超过1台,已限购");}}}}

 配置spring.xml:

spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.0.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-3.0.xsd"><context:component-scan base-package="com.csx"/><!--启用注解aop--><aop:aspectj-autoproxy/>
</beans>	

在测试类中:

APPTest

在使用JDK的动态代理时,使用的是接口接收aop创建的代理对象

public class AppTest extends TestCase
{@Testpublic void testBefore(){ApplicationContext context =new ClassPathXmlApplicationContext("spring.xml");FridgeBiz fridgeBiz =(FridgeBiz) context.getBean("fridgeBiz");fridgeBiz.buy(2,1);
//获取当前代理对象的全名System.out.println(fridgeBiz.getClass().getName());}
}

 查看结果:

CGLIB动态代理

如果想要使用CGLIB实现动态代理,根据以下步骤操作:

更新spring.xml:

spring.xml

设置 :

proxy-target-class="true"

 修改AppTest,使用实体类接收代理对象

public class AppTest extends TestCase
{@Testpublic void testBefore(){ApplicationContext context =new ClassPathXmlApplicationContext("spring.xml");
//     FridgeBiz fridgeBiz =(FridgeBiz) context.getBean("fridgeBiz");FridgeBizImpl fridgeBiz =(FridgeBizImpl) context.getBean("fridgeBiz");fridgeBiz.buy(2,1);System.out.println(fridgeBiz.getClass().getName());}
}

出现结果:

总结

spring的动态代理主要有两种:

  • JDK 动态代理:生成接口的实现类对象
  • CGLIB 动态代理:生成实现类的子类对象

相关文章:

  • 【TypeScript学习】TypeScript基础学习总结一
  • 数字教学时代:构建高效在线帮助中心的重要性
  • C嘎嘎入门篇:类和对象(2)
  • 基于JAVA Web的校园快递代领系统设计与实现(源码+定制+文档)
  • 基于单片机的温湿度检测判断系统
  • 无监督算法目标识别-工业异常检测模型Padim+PatchCore的C++_libtorch实现
  • 【Android】浅析六大设计原则
  • 拓扑结构的理解
  • NVIDIA G-Assist 项目:您的游戏和应用程序AI助手
  • 使用docker搭建zk集群
  • 【新闻转载】Storm-0501:勒索软件攻击扩展到混合云环境
  • 【源码+文档+调试讲解】交通信息管理系统
  • 用Python实现运筹学——Day 8: 对偶理论的经济解释
  • 论文阅读【时间序列】ModerTCN (ICLR2024)
  • 堆【数据结构C语言版】【 详解】
  • 《Javascript高级程序设计 (第三版)》第五章 引用类型
  • 【Leetcode】104. 二叉树的最大深度
  • Angular js 常用指令ng-if、ng-class、ng-option、ng-value、ng-click是如何使用的?
  • co.js - 让异步代码同步化
  • docker-consul
  • Facebook AccountKit 接入的坑点
  • Javascript弹出层-初探
  • Kibana配置logstash,报表一体化
  • MySQL几个简单SQL的优化
  • MYSQL如何对数据进行自动化升级--以如果某数据表存在并且某字段不存在时则执行更新操作为例...
  • Python_OOP
  • vagrant 添加本地 box 安装 laravel homestead
  • 初识MongoDB分片
  • 得到一个数组中任意X个元素的所有组合 即C(n,m)
  • 工作踩坑系列——https访问遇到“已阻止载入混合活动内容”
  • 关于字符编码你应该知道的事情
  • 模仿 Go Sort 排序接口实现的自定义排序
  • 区块链分支循环
  • 数据可视化之 Sankey 桑基图的实现
  • 详解NodeJs流之一
  • 掌握面试——弹出框的实现(一道题中包含布局/js设计模式)
  • k8s使用glusterfs实现动态持久化存储
  • 分布式关系型数据库服务 DRDS 支持显示的 Prepare 及逻辑库锁功能等多项能力 ...
  • ​力扣解法汇总946-验证栈序列
  • # dbt source dbt source freshness命令详解
  • #[Composer学习笔记]Part1:安装composer并通过composer创建一个项目
  • (9)目标检测_SSD的原理
  • (delphi11最新学习资料) Object Pascal 学习笔记---第8章第2节(共同的基类)
  • (ISPRS,2021)具有遥感知识图谱的鲁棒深度对齐网络用于零样本和广义零样本遥感图像场景分类
  • (二)基于wpr_simulation 的Ros机器人运动控制,gazebo仿真
  • (二)什么是Vite——Vite 和 Webpack 区别(冷启动)
  • (附源码)spring boot车辆管理系统 毕业设计 031034
  • (离散数学)逻辑连接词
  • (三)SvelteKit教程:layout 文件
  • (实测可用)(3)Git的使用——RT Thread Stdio添加的软件包,github与gitee冲突造成无法上传文件到gitee
  • (转)GCC在C语言中内嵌汇编 asm __volatile__
  • **CentOS7安装Maven**
  • .DFS.
  • .java 指数平滑_转载:二次指数平滑法求预测值的Java代码
  • .md即markdown文件的基本常用编写语法