使用Spring的AOP
使用Spring的AOP
- 一、AOP 的常用注解
- 1.切面类@Aspect
- 2.@Pointcut
- 3.前置通知@Before
- 4.后置通知@AfterReturning
- 5.环绕通知@Around
- 6.异常通知@AfterThrowing
- 7.最终通知@After
- 8.切面顺序@Order
- 9.启用自动代理@EnableAspectJAutoProxy
- 二、AOP注解方式开发
- 三、AOP 全注解开发
- 四、基于XML配置方式的AOP(了解)
- Spring 对
AOP
的实现包括以下3种方式:- 第一种方式:Spring框架结合AspectJ框架实现的AOP,基于注解方式。
- 第二种方式:Spring框架结合AspectJ框架实现的AOP,基于XML方式。
- 第三种方式:Spring框架自己实现的AOP,基于XML方式。
- 实际开发种都是Spring + AspectJ来实现的AOP。
- 什么是AspectJ?(Eclipse组织的一个支持AOP的框架。AspectJ框架是独立于Spring框架之外的一个框架,Spring框架用了AspectJ)
- AspectJ项目起源于帕洛阿尔托(Palo Alto)研究中心(缩写为PARC)。该中心由Xerox集团资助,Gregor Kiczales领导,从1997年开始致力于AspectJ的开发,1998年第一次发布给外部用户,2001年发布1.0 release。为了推动AspectJ技术和社团的发展,PARC在2003年3月正式将AspectJ项目移交给了Eclipse组织,因为AspectJ的发展和受关注程度大大超出了PARC的预期,他们已经无力继续维持它的发展。
一、AOP 的常用注解
1.切面类@Aspect
-
@Aspect
作用是把当前类标识为一个切面供容器读取。@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) public @interface Aspect {String value() default ""; }
2.@Pointcut
@Pointcut
注解标注在方法上面,用来定义切入点。- 可以这样做:将切点表达式单独的定义出来,在需要的位置引入即可。
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface Pointcut {String value() default "";String argNames() default ""; }
3.前置通知@Before
@Before
目标方法执行之前的通知@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface Before {String value();String argNames() default ""; }
4.后置通知@AfterReturning
@AfterReturning
目标方法执行之后的通知@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface AfterReturning {String value() default "";String pointcut() default "";String returning() default "";String argNames() default ""; }
5.环绕通知@Around
@Around
目标方法之前添加通知,同时目标方法执行之后添加通知。@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface Around {String value();String argNames() default ""; }
6.异常通知@AfterThrowing
@AfterThrowing
发生异常之后执行的通知@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface AfterThrowing {String value() default "";String pointcut() default "";String throwing() default "";String argNames() default ""; }
7.最终通知@After
@After
放在finally语句块中的通知@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface After {String value();String argNames() default ""; }
8.切面顺序@Order
- 我们知道,业务流程当中不一定只有一个切面,可能有的切面控制事务,有的记录日志,有的进行安全控制,如果多个切面的话,顺序如何控制:
可以使用@Order注解来标识切面类,为@Order注解的value指定一个整数型的数字,数字越小,优先级越高。
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD}) @Documented public @interface Order {/*** The order value.* <p>Default is {@link Ordered#LOWEST_PRECEDENCE}.* @see Ordered#getOrder()*/int value() default Ordered.LOWEST_PRECEDENCE; }
9.启用自动代理@EnableAspectJAutoProxy
- 开启自动代理之后,凡事带有@Aspect注解的bean都会生成代理对象。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AspectJAutoProxyRegistrar.class) public @interface EnableAspectJAutoProxy {boolean proxyTargetClass() default false;boolean exposeProxy() default false; }
二、AOP注解方式开发
-
注意:本文使用了
log4j2
日志,如果不知道可以看我的博客 ===> Spring对IoC的实现中的第一个Spring程序 -
注意本文也使用了
junit
进行单元测试。 -
使用
Spring+AspectJ
的AOP
需要引入的依赖如下:<!--spring的核心依赖 aop core beans jcl expression 等--> <dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>6.1.4</version> </dependency> <!-- AOP 依赖的AspectJ --> <dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>6.1.4</version> </dependency>
-
Spring配置文件中添加context命名空间和aop命名空间
<?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:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"></beans>
-
第一步:定义目标类以及目标方法
package com.gdb.service;import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service;//目标类 @Service public class OrderService {private static final Logger logger = LoggerFactory.getLogger(OrderService.class);//目标方法public void detail() {logger.info("正在打印订单详情......");} }
-
第二步:编写切面类
package com.gdb.aspect;import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component;//切面类(通知+切点 = 切面) @Aspect @Component public class MyAspect {private static final Logger logger = LoggerFactory.getLogger(MyAspect.class);//这是需要增强的代码(通知)@Before("execution(* com.gdb.service..* (..))") // com.gdb.service包下的所有方法public void beforeAdvice() {logger.info("前置通知执行了");}@AfterReturning("execution(* com.gdb.service..* (..))") // com.gdb.service包下的所有方法,public void afterReturningAdvice() {logger.info("后置通知执行了");}@Around("execution(* com.gdb.service..* (..))") // com.gdb.service包下的所有方法,public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {logger.info("前置环绕通知执行了");proceedingJoinPoint.proceed(); // 执行目标方法logger.info("后置环绕通知执行了");}@AfterThrowing("execution(* com.gdb.service..* (..))") // com.gdb.service包下的所有方法,public void afterThrowingAdvice() {logger.info("异常通知执行了");}@After("execution(* com.gdb.service..* (..))") // com.gdb.service包下的所有方法,public void afterAdvice() {logger.info("最终通知执行了");} }
-
第三步:在配置文件中启动包扫描启用自动代理
<?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:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><!--开启组件扫描--><context:component-scan base-package="com.gdb"/><!--开启自动代理--><aop:aspectj-autoproxy proxy-target-class="false"/><!--<aop:aspectj-autoproxy proxy-target-class="true"/> 开启自动代理之后,凡事带有@Aspect注解的bean都会生成代理对象。proxy-target-class="true" 表示采用cglib动态代理。proxy-target-class="false" 表示采用jdk动态代理。默认值是false。即使写成false,当没有接口的时候,也会自动选择cglib生成代理类。--> </beans>
-
第四步:编写测试程序
@Test public void test(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");OrderService orderService = applicationContext.getBean("orderService", OrderService.class);orderService.detail(); }
-
第五步:执行结果
- 通过上面的执行结果就可以判断他们的执行顺序了,这里不再赘述。
-
第六步:结果中没有异常通知,这是因为目标程序执行过程中没有发生异常。我们尝试让目标方法发生异常:
package com.gdb.service;import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service;@Service public class OrderService {private static final Logger logger = LoggerFactory.getLogger(OrderService.class);public void detail() {logger.info("正在打印订单详情......");throw new RuntimeException();} }
-
第七步:执行结果:
- 通过测试得知,当发生异常之后,最终通知也会执行,因为最终通知
@After
会出现在finally
语句块中。出现异常之后,后置通知
和环绕通知的结束部分
不会执行。
- 通过测试得知,当发生异常之后,最终通知也会执行,因为最终通知
-
优化使用切点表达式:
- 上面编写的切面类的缺点是:
- 第一:切点表达式重复写了多次,没有得到复用。
- 第二:如果要修改切点表达式,需要修改多处,难维护。
- 上面编写的切面类的缺点是:
-
可以这样做:将切点表达式单独的定义出来,在需要的位置引入即可。
package com.gdb.aspect;import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component;@Aspect @Component public class MyAspect {private static final Logger logger = LoggerFactory.getLogger(MyAspect.class);@Pointcut("execution(* com.gdb.service..* (..))")public void pointcut() {}@Before("pointcut()") // com.gdb.service包下的所有方法public void beforeAdvice() {logger.info("前置通知执行了");}@AfterReturning("pointcut()") // com.gdb.service包下的所有方法,public void afterReturningAdvice() {logger.info("后置通知执行了");}@Around("pointcut()") // com.gdb.service包下的所有方法,public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {logger.info("前置环绕通知执行了");proceedingJoinPoint.proceed();logger.info("后置环绕通知执行了");}@AfterThrowing("pointcut()") // com.gdb.service包下的所有方法,public void afterThrowingAdvice() {logger.info("异常通知执行了");}@After("pointcut()") // com.gdb.service包下的所有方法,public void afterAdvice() {logger.info("最终通知执行了");} }
-
使用@Pointcut注解来定义独立的切点表达式。
-
注意这个@Pointcut注解标注的方法随意,只是起到一个能够让@Pointcut注解编写的位置。
三、AOP 全注解开发
- 第一步:
就是编写一个类,在这个类上面使用大量注解来代替spring的配置文件,spring配置文件消失了
,如下:package com.gdb.config;import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy;@Configuration // 配置类 @ComponentScan("com.gdb") @EnableAspectJAutoProxy(proxyTargetClass = false) public class SpringConfig { }
- 第二步:测试程序也变化了
@Test public void test() {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);OrderService orderService = applicationContext.getBean("orderService", OrderService.class);orderService.detail(); }
- 第三步:执行结果
四、基于XML配置方式的AOP(了解)
- 第一步:编写目标类
package com.gdb.service;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class OrderService {private static final Logger logger = LoggerFactory.getLogger(OrderService.class);public void detail() {logger.info("正在打印订单详情......");}
}
- 第二步:编写切面类,并且编写通知
package com.gdb.aspect;import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class MyAspect {private static final Logger logger = LoggerFactory.getLogger(MyAspect.class);public void beforeAdvice() {logger.info("前置通知执行了");}public void afterReturningAdvice() {logger.info("后置通知执行了");}public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {logger.info("前置环绕通知执行了");proceedingJoinPoint.proceed();logger.info("后置环绕通知执行了");}public void afterThrowingAdvice() {logger.info("异常通知执行了");}public void afterAdvice() {logger.info("最终通知执行了");}
}
- 第三步:编写spring配置文件
<?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:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><bean id="myAspect" class="com.gdb.aspect.MyAspect"/><bean id="orderService" class="com.gdb.service.OrderService"/><!--aop配置--><aop:config><!--切点表达式--><aop:pointcut id="p" expression="execution(* com.gdb.service..* (..))"/><!--切面--><aop:aspect ref="myAspect"><!--切面=通知 + 切点--><aop:before method="beforeAdvice" pointcut-ref="p"/><aop:after-returning method="afterReturningAdvice" pointcut-ref="p"/><aop:around method="aroundAdvice" pointcut-ref="p"/><aop:after-throwing method="afterThrowingAdvice" pointcut-ref="p"/><aop:after method="afterAdvice" pointcut-ref="p"/></aop:aspect></aop:config>
</beans>
- 第四步:编写测试程序
@Test
public void test() {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");OrderService orderService = applicationContext.getBean("orderService", OrderService.class);orderService.detail();
}
- 第四步:执行结果
通过结果可以看出来顺序和前面的不一样了,我感觉是配置文件中的顺序有关系,由于主要都是使用注解的方式。
相关文章:

使用Spring的AOP
使用Spring的AOP 一、AOP 的常用注解1.切面类Aspect2.Pointcut3.前置通知Before4.后置通知AfterReturning5.环绕通知Around6.异常通知AfterThrowing7.最终通知After8.切面顺序Order9.启用自动代理EnableAspectJAutoProxy 二、AOP注解方式开发三、AOP 全注解开发四、基于XML配置…...
爬虫之矛---JavaScript基石篇3<JavaScript构造函数的内部机制和应用(2)>
前言: 继续上一篇https://blog.csdn.net/m0_56758840/article/details/136592611 正文: 1.ES6中的类和构造函数的对应关系 A. 介绍ES6引入的类的概念和语法糖 类的概念: ES6引入了类(class)的概念,类是一种抽象的数据类型&…...
_note_05
1.说一说什么是函数重载? 函数签名相同除了 形参不同数据类型 函数签名相同除了 形参不同个数 2.void关键字的作用?返回值是void ,可以写return 吗? 函数无返回,使用void修饰; 可以只使用return使函数结束; 3.按要…...
将格蠹GDK8的cmake3.10升级为cmake3.15
#升级过程# 1、wget https://cmake.org/files/v3.15/cmake-3.15.0-rc1.tar.gz 2、tar -zxvf cmake-3.15.0-rc1.tar.gz 3 、cd cmake-3.15.0-rc1 4、./configure 5、sudo make install 6、reboot 7、查看cmake版本: geduergdk8:~$ cmake --version cmake ve…...

b树(一篇文章带你 理解 )
目录 一、引言 二、B树的基本定义 三、B树的性质与操作 1 查找操作 2 插入操作 3 删除操作 四、B树的应用场景 1 数据库索引 2 文件系统 3 网络路由表 五、哪些数据库系统不使用B树进行索引 1 列式数据库 2 图形数据库 3 内存数据库 4 NoSQL数据库 5 分布式数据…...

OD_2024_C卷_200分_7、5G网络建设【JAVA】【最小生成树】
package odjava;import java.util.Scanner;public class 七_5G网络建设 {public static void main(String[] args) {Scanner sc new Scanner(System.in);int n sc.nextInt(); // 基站数量(节点数)int m sc.nextInt(); // 基站对数量(边数&…...

面试题:分布式锁用了 Redis 的什么数据结构
在使用 Redis 实现分布式锁时,通常使用 Redis 的字符串(String)。Redis 的字符串是最基本的数据类型,一个键对应一个值,它能够存储任何形式的字符串,包括二进制数据。字符串类型的值最多可以是 512MB。 Re…...

【学习心得】websocket协议简介并与http协议对比
一、轮询和长轮询 在websocket协议出现之前,要想实现服务器和客户端的双向持久通信采取的是Ajax轮询。它的原理是每隔一段时间客户端就给服务器发送请求找服务器要数据。 让我们通过一个生活化的比喻来解释轮询和长轮询假设你正在与一位不怎么主动说话的老大爷&…...

基于Token的身份验证:安全与效率的结合
🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…...

Electron程序如何在MacOS下获取相册访问权限
1.通过entitiment.plist,在electron-builder签名打包时,给app包打上签名。最后可以通过codesign命令进行验证。 TestPhotos.plist electron-builder配置文件中加上刚刚的plist文件。 通过codesign命令验证,若出现这个,则说明成…...

uniapp让输入框保持聚焦状态,不会失去焦点
使用场景:当输入框还有发送按钮的时候,点击发送希望软键盘不消失,还可以继续输入,或者避免因输入图片标签造成的屏闪问题 多次尝试后发现一个很实用的方法,适用input输入框和editor输入框 解决办法:把cli…...
面试中如何介绍mysql的B+树
B树是B树的变体,也是一颗多路搜索树。在MySQL中,B树是为磁盘或者其他直接辅助存储设备所设计的一种平衡的查找树结构。其具有以下特点: 每个节点最多有m个子女,m阶的B树深度最多为m。非根节点关键值个数范围是⌈m/2⌉-1<k<m…...

【Linux C | 网络编程】多播的概念、多播地址、UDP实现多播的C语言例子
😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀 🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C、数据结构、音视频🍭 🤣本文内容🤣&a…...

AIGC实战——GPT(Generative Pre-trained Transformer)
AIGC实战——GPT 0. 前言1. GPT 简介2. 葡萄酒评论数据集3. 注意力机制3.1 查询、键和值3.2 多头注意力3.3 因果掩码 4. Transformer4.1 Transformer 块4.2 位置编码 5. 训练GPT6. GPT 分析6.1 生成文本6.2 注意力分数 小结系列链接 0. 前言 注意力机制能够用于构建先进的文本…...

微信小程序-入门
一.通过 Npm方式下载构建 1.下载和安装Npm:Npm https://docs.npmjs.com/downloading-and-installing-node-js-and-npm 或者 https://nodejs.org/en/download/ 未安装npm 提示 以下以安装node安装包为例 按任意键继续 安装完成后 2. 下载和安装小程序开…...
0102全排列和对换-行列式-线性代数
把n个不同的数排成一列,叫做这n个数的全排列(排列)。 一般情况, 1 , 2 , ⋯ , n 1,2,\cdots,n 1,2,⋯,n是n个数排列的标准次序。 当n个数的任一排列中两个数的先后次序与标准次序不同时,有说有一个逆序。 一个排列中所…...

面向对象的编程语言是什么意思?——跟老吕学Python编程
面向对象的编程语言是什么意思?——跟老吕学Python编程 面向对象是什么意思?面向对象的定义面向对象的早期发展面向对象的背景1.审视问题域的视角2.抽象级别3.封装体4.可重用性 面向对象的特征面向对象的开发方法面向对象程序设计基本思想实现 面向对象的…...

Spring Boot整合MyBatis Plus配置多数据源
Spring Boot 专栏:https://blog.csdn.net/dkbnull/category_9278145.html Spring Cloud 专栏:https://blog.csdn.net/dkbnull/category_9287932.html GitHub:https://github.com/dkbnull/SpringBootDemo Gitee:https://gitee.com/…...
Unix Network Programming Episode 88
‘inetd’ Daemon On a typical Unix system, there could be many servers in existence, just waiting for a client request to arrive. Examples are FTP, Telnet, Rlogin, TFTP, and so on. With systems before 4.3BSD, each of these services had a process associate…...
Java面试题之11MySQL
你对MySQL执行计划怎么看 执行计划就是SQL的执行查询的顺序,以及如何使用索引查询,返回的结果集的行数 在MySQL中,我们可以通过explain命令来查看执行计划。其语法如下: EXPLAIN SELECT * FROM table_name WHERE conditions;在…...

使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...

微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...

PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...

网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
在 Spring Boot 项目里,MYSQL中json类型字段使用
前言: 因为程序特殊需求导致,需要mysql数据库存储json类型数据,因此记录一下使用流程 1.java实体中新增字段 private List<User> users 2.增加mybatis-plus注解 TableField(typeHandler FastjsonTypeHandler.class) private Lis…...

【LeetCode】算法详解#6 ---除自身以外数组的乘积
1.题目介绍 给定一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O…...