Spring AOP面向切面编程
Spring AOP面向切面编程
- 面向切面编程
- AOP作用
- AOP功能
- AOP总结
- AOP核心概念
- AOP的实现方式
- Spring 对AOP支持
- 支持@Aspect
- 声明一个切面
- 声明一个切入点
- AspectJ描述符如下
- AspectJ类型匹配的通配符
- 常用的匹配规则
- 声明增强
- 用AOP实现日志拦截
- 一般的实现
- 仅拦截需要的方法
- 先定义一个日志注解Log
- 用@annotation定义切入点
- 在想做日志输出的方法上使用注解Log
- requestId传递
- 关于增强执行顺序
面向切面编程
面向切面编程(
AOP,Aspect Oriented Programming
)是通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。
AOP作用
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP功能
主要功能:日志记录、性能统计、安全控制、事务处理、异常处理等。
AOP总结
面向切面编程是希望能够将通用需求功能从不相关的类当中分离出来,能够使得很多类共享一个行为,一旦发生变化,不必修改很多类,而只是修改这个行为即可。
AOP通过提供另一种思考程序结构的方式来补充了面向对象编程(OOP)。OOP中模块化的基本单元是类(class),而AOP中模块化的基本单元是切面(aspect)。可以这么理解,OOP是解决了纵向的代码复用问题,AOP是解决了横向的代码复用问题。
Spring的关键组件之一是AOP框架。虽然Spring IOC容器不依赖于AOP,意味着如果你不想使用AOP,则可以不使用AOP,但AOP补充了Spring IOC以提供一个非常强大的中间件解决方案。
AOP核心概念
- 切面(aspect): 在AOP中,切面一般使用@Aspect注解来标识。
- 连接点(Join Point): 在Spring AOP,一个连接点总是代表一次方法的执行。
- 增强(Advice): 在连接点执行的动作。
- 切入点(Pointcout): 说明如何匹配到连接点。
- 引介(Introduction): 为现有类型声明额外的方法和属性。
- 目标对象(Target Object): 由一个或者多个切面建议的对象,也被称为
建议对象
,由于Spring AOP
是通过动态代理来实现的,这个对象永远是一个代理对象。 - AOP代理(AOP proxy): 一个被AOP框架创建的对象,用于实现切面约定(增强方法的执行等)。在
Spring Framework
中,一个AOP代理
是一个JDK动态代理
或者CGLIB代理
。 - 织入(Weaving): 连接切面和目标对象或类型创建代理对象的过程。它能在编译时(例如使用
AspectJ编译器
)、加载时或者运行时完成。Spring AOP
与其他的纯Java AOP框架
一样是在运行时进行织入的。
Spring AOP包括以下类型的增强:
前置增强(Before advice)
:在连接点之前运行,但不能阻止到连接点的流程继续执行(除非抛出异常)返回增强(After returning advice)
:在连接点正常完成后运行的增强(例如,方法返回没有抛出异常)异常增强(After thorwing advice)
:如果方法抛出异常退出需要执行的增强后置增强(After (finally) Advice)
:无论连接点是正常或者异常退出,都会执行该增强环绕增强(Around advice)
:围绕连接点的增强,例如方法的调用。环绕增强能在方法的调用之前和调用之后自定义行为。它还可以选择方法是继续执行或者去缩短方法的执行通过返回自己的值或者抛出异常。
AOP的实现方式
AOP的两种实现方式:静态织入(以AspectJ为代表)和动态代理(Spring AOP实现)。
AspectJ
是一个采用Java实现的AOP
框架,它能够对代码进行编译(在编译期进行),让代码具有AspectJ
的AOP
功能,当然它也可支持动态代理的方式;Spring AOP实现
:通过动态代理技术来实现,Spring2.0集成了AspectJ
,主要用于PointCut
的解析和匹配,底层的技术还是使用的Spring1.x
中的动态代理来实现。
Spring AOP实现采用了两种混合的实现方式:JDK动态代理和CGLib动态代理。
- JDK动态代理:Spring AOP的首选方法。每当目标对象实现一个接口时,就会使用JDK动态代理。目标对象必须实现接口。
- CGLIB:如果目标对象没有实现接口,则可以使用
CGLIB
代理。
Spring 对AOP支持
Spring可以使用两种方式来实现AOP:基于注解式配置和基于XML配置。
下面介绍基于注解配置的形式
支持@Aspect
如果是Spring Framework
,需要使用aspectjweaver.jar
包,然后创建我们自己的AppConfig
,如下,并加上@EnableAspectJAutoProxy
注解开启AOP代理
自动配置(Spring Boot默认是开启的
,则不需要增加配置),如下:
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {}
声明一个切面
@Aspect //告诉Spring 这是一个切面
@Component //交给Spring容器管理
public class MyAspect {}
可以使用@Aspect
来定义一个类作为切面,但是这样,该类并不会自动被Spring加载,还是需要加上@Component
注解。
声明一个切入点
一个切入点的生命包含两个部分:一个包含名称和任何参数的签名和一个切入点的表达式,这个表达式确定了我们对哪些方法的执行感兴趣。
我们以拦截Controller
层中的MyController
中的test方法为例子,代码如下:
@RestController
@RequestMapping("/my")
public class MyController {@GetMapping("/test")public void test() {System.out.println("test 方法");}
}
下面定义一个名为controller
的切入点,该切入点与上述的test方法相匹配,切入点需要用@Pointcut
注解来标注,如下:
//表达式
@Pointcut("execution (public * com.yc.springboot.controller.MyController.test())")
public void controller(){}; //签名
切入点表达式的格式如下:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
execution(方法修饰符(可选) 返回类型 类路径 方法名 参数 异常模式(可选))
AspectJ描述符如下
AspectJ描述符 | 描述 |
---|---|
arg() | 限制连接点匹配参数为指定类型的执行方法 |
@args() | 限制连接点匹配参数由指定注解标注的执行方法 |
execution() | 用于匹配是连接点的执行方法 |
this() | 限制连接点匹配的AOP代理的bean引用为指定类型的类 |
target | 限制连接点匹配目标对象为指定类型的类 |
@target() | 限制连接点匹配特定的执行对象,这些对象对应的类要具有指定类型的注解 |
within() | 限制连接点匹配指定的类型 |
@within() | 限制连接点匹配指定注解所标注的类型 |
@annotationn | 限定匹配带有指定注解的连接点 |
常用的主要是:execution()
AspectJ类型匹配的通配符
通配符 | 含义 |
---|---|
* | 匹配任何数量字符 |
.. | 匹配任何数量字符的重复 |
+ | 匹配指定类型的子类型;仅能用于后缀放在类型模式后面 |
常用的匹配规则
表达式 | 内容 |
---|---|
execution(public * *(..)) | 匹配所有public方法 |
execution(* set*(..)) | 匹配所有方法名开头为set的方法 |
execution(* com.xyz.service.AccountService.*(..)) | 匹配AccountService下的所有方法 |
execution(* com.xyz.service.*.*(..)) | 匹配service包下的所有方法 |
execution(* com.xyz.service..*.*(..)) | 匹配service包或其子包下的所有方法 |
@annotation(org.springframework.transaction.annotation.Transactional) | 匹配所有打了@Transactional注解的方法 |
bean(*Service) | 匹配命名后缀为Service的类的方法 |
声明增强
增强与切点表达式相关联,并且在与切点匹配的方法之前、之后或者前后执行。
下面直接罗列了各种增强的声明,用于拦截MyController中的各个方法
@Aspect //告诉Spring 这是一个切面
@Component //告诉Spring容器需要管理该对象
public class MyAspect {//通过规则确定哪些方法是需要增强的@Pointcut("execution (public * com.yc.springboot.controller.MyController.*(..))")public void controller() {}//前置增强@Before("controller()")public void before(JoinPoint joinPoint) {System.out.println("before advice");}//返回增强@AfterReturning(pointcut = "controller()",returning = "retVal")public void afterReturning(Object retVal) {System.out.println("after returning advice, 返回结果 retVal:" + retVal);}//异常增强@AfterThrowing(pointcut = "controller()",throwing = "ex")public void afterThrowing(Exception ex) {System.out.println("after throwing advice, 异常 ex:" + ex.getMessage());}//后置增强@After("controller()")public void after(JoinPoint joinPoint) {System.out.println("after advice");}//环绕增强@Around("controller()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println("before advice");//相当于是before adviceObject reVal = null;try {reVal = joinPoint.proceed();} catch (Exception e) {//相当于afterthrowing adviceSystem.out.println("afterthrowing advice");}//相当于是after adviceSystem.out.println("after advice");return reVal;}
}
需要注意的是:
- 在返回增强中,我们需要给
@AfterReturing
设置returning
的值,且需要与方法的参数名一致,用于表示业务方法的返回值 - 在异常增强中,需要给
@AfterThrowing
设置throwing
的值,且需要与方法的参数名一致,用于表示业务方法产生的异常 - 在环绕增强中,参数为
ProceedingJoinPoint
类型,它是JoinPoint
的子接口,我们需要在这个方法中手动调用其proceed方法来触发业务方法 - 在所有的增强方法中都可以申明第一个参数为
JoinPoint
(注意的是,环绕增强是使用ProceedingJoinPoint
来进行申明,它实现了JoinPoint
接口) JoinPoint
接口提供了几个有用的方法- getArgs():返回这个方法的参数
- getThis():返回这个代理对象
- getTarget():返回目标对象(被代理的对象)
- getSignature():返回被增强方法的描述
- toString():打印被增强方法的有用描述
下面为Mycontroller测试类:
@RestController
@RequestMapping("/my")
public class MyController {@GetMapping("/testBefore")public void testBefore() {System.out.println("testBefore 业务方法");}@GetMapping("/testAfterReturning")public String testAfterReturning() {System.out.println("testAfterReturning 业务方法");return "我是一个返回值";}@GetMapping("/testAfterThrowing")public void testAfterThrowing() {System.out.println("testAfterThrowing 业务方法");int a = 0;int b = 1 / a;}@GetMapping("/testAfter")public void testAfter() {System.out.println("testAfter 业务方法");}@GetMapping("/around")public void around() {System.out.println("around 业务方法");}
}
用AOP实现日志拦截
一般的实现
打印日志是AOP
的一个常见应用场景,我们可以对Controller
层向外提供的接口做统一的日志拦截,用日志记录请求参数、返回参数、请求时长以及异常信息,方便我们线上排查问题,下面是核心类LogAspect
的实现
/*** 日志的切面*/
@Aspect
@Component
public class LogAspect {@Resourceprivate IdWorker idWorker;@Pointcut("execution (public * com.yc.core.controller.*.*(..))")public void log(){}/*** 使用环绕增强实现日志打印* @param joinPoint* @return* @throws Throwable*/@Around("log()")public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {//获得执行方法的类和名称String className = joinPoint.getTarget().getClass().getName();//获得方法名MethodSignature signature = (MethodSignature) joinPoint.getSignature();String methodName = signature.getMethod().getName();//获得参数Object[] args = joinPoint.getArgs();long requestId = idWorker.nextId();//打印参数LogHelper.writeInfoLog(className, methodName, "requestId:" + requestId + ",params:" + JSONObject.toJSONString(args));long startTime = System.currentTimeMillis();//执行业务方法Object result = null;try {result = joinPoint.proceed();} catch (Exception e) {LogHelper.writeErrLog(className, methodName, "requestId:" + requestId + ",异常啦:" + LogAspect.getStackTrace(e));}long endTime = System.currentTimeMillis();//打印结果LogHelper.writeInfoLog(className, methodName, "requestId:" + requestId + ",耗时:" + (endTime - startTime) + "ms,result:" + JSONObject.toJSONString(result));//返回return result;}/*** 获取异常的堆栈信息* @param throwable* @return*/public static String getStackTrace(Throwable throwable){StringWriter sw = new StringWriter();PrintWriter pw = new PrintWriter(sw);try{throwable.printStackTrace(pw);return sw.toString();} finally{pw.close();}}
}
- 在
proceed()
方法之前,相当于前置增强,收集类名、方法名、参数,记录开始时间,生成requestId
- 在
proceed()
方法之后,相当于后置增强,并能获取到返回值,计算耗时 - 在前置增强时,生成
requestId
,用于串联多条日志 - 使用
try、catch
包裹proceed()
方法,在catch
中记录异常日志 - 提供了
getStackTrace
方法获取异常的堆栈信息,便于排查报错详细情况
仅拦截需要的方法
但是上面的日志是针对所有controller层
中的方法进行了日志拦截,如果我们有些方法不想进行日志输出,比如文件上传的接口、大量数据返回的接口,这个时候定义切入点的时候可以使用@annotation
描述符来匹配加了特定注解的方法,步骤如下:
先定义一个日志注解Log
/*** 自定义日志注解*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
}
用@annotation定义切入点
@Pointcut("@annotation(com.yc.core.annotation.Log)")
public void logAnnotation(){}
在想做日志输出的方法上使用注解Log
@Log
@PostMapping(value = "testannotation")
public AOPTestVO testannotation(@RequestBody AOPTestDTO aopTestDTO) {AOPTestVO aopTestVO = new AOPTestVO();aopTestVO.setCode(1);aopTestVO.setMsg("哈哈哈");return aopTestVO;
}
这样,我们就可以自定义哪些方法需要日志输出了。
requestId传递
后来有同事提到,如果这是针对Controller层的拦截,但是Service层也有自定义的日志输出,怎么在Service层获取到上述的requestId
呢?
其实就是我们拦截之后,是否可以针对方法的参数进行修改呢?其实注意是看
result = joinPoint.proceed();
我们发现ProceedingJoinPoint
还有另外一个带有参数的proceed方法
,定义如下:
public Object proceed(Object[] args) throws Throwable;
我们可以利用这个方法,在环绕增强中去增加requestId
,这样后面的增强方法或业务方法中就能获取到这个requestId
了。
首先,我们先定义一个基类AOPBaseDTO
,只有一个属性requestId
@Data
@ApiModel("aop参数基类")
public class AOPBaseDTO {@ApiModelProperty(value = "请求id", hidden = true)private long requestId;
}
然后我们让Controller
层接口的参数AOPTestDTO
继承上述AOPBaseDTO
,如下:
@Data
@ApiModel("aop测试类")
public class AOPTestDTO extends AOPBaseDTO{@ApiModelProperty(value = "姓名")private String name;@ApiModelProperty(value = "年龄")private int age;
}
最后在环绕的增强中添加上requestId
,如下:
/*** 使用环绕增强实现日志打印* @param joinPoint* @return* @throws Throwable*/
@Around("logAnnotation()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {//获得执行方法的类和名称String className = joinPoint.getTarget().getClass().getName();//获得方法名MethodSignature signature = (MethodSignature) joinPoint.getSignature();String methodName = signature.getMethod().getName();//获得参数Object[] args = joinPoint.getArgs();long requestId = idWorker.nextId();for(int i = 0; i < args.length; i++) {if (args[i] instanceof AOPBaseDTO) {//增加requestId((AOPBaseDTO) args[i]).setRequestId(requestId);}}//打印参数LogHelper.writeInfoLog(className, methodName, "requestId:" + requestId + ",params:" + JSONObject.toJSONString(args));long startTime = System.currentTimeMillis();//执行业务方法Object result = null;try {result = joinPoint.proceed(args);} catch (Exception e) {LogHelper.writeErrLog(className, methodName, "requestId:" + requestId + ",异常啦:" + LogAspect.getStackTrace(e));}long endTime = System.currentTimeMillis();//打印结果LogHelper.writeInfoLog(className, methodName, "requestId:" + requestId + ",耗时:" + (endTime - startTime) + "ms,result:" + JSONObject.toJSONString(result));//返回return result;
}
我们运行起代码,访问一下,下面是运行结果:
可以看到,我们的业务方法中已经能获取到requestId
,如果Service层
需要,可以通过传递AOPTestDTO
,从中获取。
关于增强执行顺序
- 针对不同类型的增强,顺序固定的,比如
before在after
前面 - 针对同一切面的相同类型的增强,根据定义先后顺序依次执行
- 针对不同切面的相同增强,可以通过使我们的切面实现
Ordered接口
,重写getOrder
方法,返回值最小,优先级越高。注意的是before和after
的相反的,before
的优先级越高越早执行,after
的优先级越高,越晚执行。
相关文章:

Spring AOP面向切面编程
Spring AOP面向切面编程 面向切面编程AOP作用AOP功能AOP总结 AOP核心概念AOP的实现方式Spring 对AOP支持支持Aspect声明一个切面声明一个切入点AspectJ描述符如下AspectJ类型匹配的通配符常用的匹配规则 声明增强 用AOP实现日志拦截一般的实现仅拦截需要的方法先定义一个日志注…...

Visual Studio 中增加的AI功能
前言: 人工智能的发展,在现在,编程技术的IDE里面也融合了AI的基本操做。本例,以微软的Visual Studio中的人工智能的功能介绍例子。 本例的环境: Visual Studio 17.12 1 AI 智能变量检测: 上图展示了一…...
15. 接雨水
接雨水 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 示例 1: 输入:height = [0,1,0,2,1,0,1,3,2,1,2,1] 输出:6 解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(…...

从索尼爱立信手机打印短信的简单方法
昨天,我买了一部新手机来代替我的旧索尼爱立信Xperia,但手机上有很多珍贵的短信,是我男朋友发来的,我不想失去它们。然后我尝试打印它们,但我无法从我的索尼爱立信手机中取出它们。您有什么从索尼爱立信手机打印短信的…...
Java-list均分分割到多个子列表
在Java中,如果你有一个List并且想要将其均分到多个子列表中,可以使用以下方法。假设你有一 个List<T>,并且想要将其分成n个子列表。 import java.util.ArrayList; import java.util.List;public class ListSplitter {public static <T> List<List<T>…...

kettle合并表数据
总体执行图:以两个数据表作为输入,根据关键栏位进行合并后,以excel表输出。 两表数据输入 需要确定查询的表名 2. 根据关键栏位进行排序。在记录集连接之前需要进行排序操作 3. 记录连接与合并 此方式表示select EQP_ID, ID FROM T_EQP_C…...

蓝耘平台使用InstantMesh生成高质量的三维网格模型!3D内容创作!小白入门必看!!!
目录 引言 InstantMesh应用介绍 蓝耘平台与InstantMesh结合使用 如何部署(超简单) 第一步登录蓝耘平台 第二步点击应用商城 编辑 第三步选择InstantMesh 第四步点击部署 第五步点击快速启动应用 第六步即可体验该产品 总结 注册链接 引言…...

关于IDE的相关知识之二【插件推荐】
成长路上不孤单😊😊😊😊😊😊 【14后😊///计算机爱好者😊///持续分享所学😊///如有需要欢迎收藏转发///😊】 今日分享关于ide插件推荐的相关内容!…...

oceanbase集群访问异常问题处理
1.报错现象 2.问题排查 检查obproxy状态发现为不可用状态 重启obproxy 依次重启Obproxy集群 观察任务状态 重启完成 Obproxy状态正常 3.验证登录 登录成功...

Linux(centos)安装 MySQL 8 数据库(图文详细教程)
前言 前几天写了个window系统下安装Mysql的博客,收到很多小伙伴私信需要Linux下安装Mysql的教程,今天这边和大家分享一下,话不多说,看教程。 一、删除以前安装的MySQL服务 一般安装程序第一步都需要清除之前的安装痕迹ÿ…...

C++之map和set的模拟实现
目录 引言 红黑树迭代器实现 红黑树元素的插入 map模拟实现 set模拟实现 之前我们已经学习了map和set的基本使用,但是因为map和set的底层都是用红黑树进行封装实现的,上期我们已经学习了红黑树的模拟实现,所以本期我们在红黑树模拟实现…...
判断一个单链表是否是回文结构 要求O(N)时间复杂度 O(1)空间复杂度
没做出来 看了解析 但是思路想到了 就是只能调整链表顺序,正确答案是 把链表变成两条单链表,分别从两侧走向中间拿两个指针 分别指向两头 ,往中间走 中途有不一样的就返回false, private static boolean handle(Node head){int size size…...
Kafka 快速实战及基本原理详解解析-01
一、Kafka 介绍 1. MQ 的作用 消息队列(Message Queue,简称 MQ)是一种用于跨进程通信的技术,核心功能是通过异步消息的方式实现系统之间的解耦。它在现代分布式系统中有着广泛的应用,主要作用体现在以下三个方面&…...

wujie无界微前端框架初使用
先说一下项目需求:将单独的四套系统的登录操作统一放在一个入口页面进行登录,所有系统都使用的是vue3,(不要问我为啥会这样设计,产品说的客户要求) 1.主系统下载wujie 我全套都是vue3,所以直接…...
C++ 设计模式:职责链模式(Chain of Responsibility)
链接:C 设计模式 链接:C 设计模式 - 组合模式 链接:C 设计模式 - 迭代器模式 职责链模式(Chain of Responsibility Pattern)是一种行为型设计模式,它允许多个对象都有机会处理请求,从而避免请求…...

Yocto项目 - 详解PACKAGECONFIG机制
引言 Yocto项目是一个强大的嵌入式Linux开发工具,广泛应用于创建定制的嵌入式Linux发行版。在Yocto中,配置和定制化构建系统、软件包、以及生成适用于特定硬件的平台镜像是非常重要的。PACKAGECONFIG是Yocto项目中用于灵活启用或禁用软件包特性的强大工…...

Linux下部署ElasticSearch集群
Elasticsearch7.17.8集群的搭建 节点host名称节点ip节点部署内容k8s-m192.168.40.142主节点 数据节点k8s-w1192.168.40.141主节点 数据节点k8s-w2192.168.40.140数据节点 一、准备安装环境 1.下载安装包 官网 www.elastic.co 下载所有版本地址 点击跳转 下载elasticsearch-7.…...
超高分辨率 图像 分割处理
文章大纲 制造业半导体领域高分辨率图像半导体数据集开源的高分辨率晶圆图像数据集1. WM-811K数据集2. Kaggle上的WM-811K Clean Subset数据集医疗 病理领域高分辨率图像1. Camelyon+2. CAMELYON173. CPIA Dataset4. UCF-WSI-Dataset航拍 遥感中的高分辨率 图像航拍遥感领域高分…...

【含文档+PPT+源码】基于springboot的农贸菜市场租位管理系统的设计与实现
开题报告 本文旨在探讨基于SpringBoot框架构建的农贸菜市场租位管理系统的设计与实现。系统结合了现代化信息技术与农贸市场管理需求,为用户提供了注册登录、查看系统公告、分类搜索店铺、查看店铺详情、填写租赁信息、在线租赁、我的订单管理以及用户信息和密码修…...

信息科技伦理与道德1:绪论
1 问题描述 1.1 信息科技的进步给人类生活带来的是什么呢? 功能?智能?陪伴?乐趣?幸福? 基于GPT-3的对话Demo DeepFake 深伪技术:通过神经网络技术进行大样本学习,将个人的声音、面…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

代码规范和架构【立芯理论一】(2025.06.08)
1、代码规范的目标 代码简洁精炼、美观,可持续性好高效率高复用,可移植性好高内聚,低耦合没有冗余规范性,代码有规可循,可以看出自己当时的思考过程特殊排版,特殊语法,特殊指令,必须…...

STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...

协议转换利器,profinet转ethercat网关的两大派系,各有千秋
随着工业以太网的发展,其高效、便捷、协议开放、易于冗余等诸多优点,被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口,具有实时性、开放性,使用TCP/IP和IT标准,符合基于工业以太网的…...
Docker拉取MySQL后数据库连接失败的解决方案
在使用Docker部署MySQL时,拉取并启动容器后,有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致,包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因,并提供解决方案。 一、确认MySQL容器的运行状态 …...

Android写一个捕获全局异常的工具类
项目开发和实际运行过程中难免会遇到异常发生,系统提供了一个可以捕获全局异常的工具Uncaughtexceptionhandler,它是Thread的子类(就是package java.lang;里线程的Thread)。本文将利用它将设备信息、报错信息以及错误的发生时间都…...

pgsql:还原数据库后出现重复序列导致“more than one owned sequence found“报错问题的解决
问题: pgsql数据库通过备份数据库文件进行还原时,如果表中有自增序列,还原后可能会出现重复的序列,此时若向表中插入新行时会出现“more than one owned sequence found”的报错提示。 点击菜单“其它”-》“序列”,…...

客户案例 | 短视频点播企业海外视频加速与成本优化:MediaPackage+Cloudfront 技术重构实践
01技术背景与业务挑战 某短视频点播企业深耕国内用户市场,但其后台应用系统部署于东南亚印尼 IDC 机房。 随着业务规模扩大,传统架构已较难满足当前企业发展的需求,企业面临着三重挑战: ① 业务:国内用户访问海外服…...