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

AspectJ 下 Advisor 的排序过程

在 AbstractAdvisorAutoProxyCreator#findEligibleAdvisors 方法中,找到 BeanFactory 中所有的 Advisor 后,针对当前 beanClass 进行过滤,筛选出符合当前 beanClass 的 Advisor,称之为 eligibleAdvisors。接着对 eligibleAdvisors 进行扩展,AspectJ 下会在集合首位添加一个 ExposeInvocationInterceptor.ADVISOR,这是一个 DefaultPointcutAdvisor,持有的 Advice 为一个 ExposeInvocationInterceptor 对象。接着对这个 eligibleAdvisors 进行排序。

// AbstractAdvisorAutoProxyCreator
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {List<Advisor> candidateAdvisors = findCandidateAdvisors();List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);extendAdvisors(eligibleAdvisors);if (!eligibleAdvisors.isEmpty()) {eligibleAdvisors = sortAdvisors(eligibleAdvisors);}return eligibleAdvisors;
}
// AspectJAwareAdvisorAutoProxyCreator  
@Override
protected List<Advisor> sortAdvisors(List<Advisor> advisors) {List<PartiallyComparableAdvisorHolder> partiallyComparableAdvisors = new ArrayList<>(advisors.size());// 封装 PartiallyComparableAdvisorHolder 集合for (Advisor advisor : advisors) {// 默认的优先级比较器 AspectJPrecedenceComparatorpartiallyComparableAdvisors.add(new PartiallyComparableAdvisorHolder(advisor, DEFAULT_PRECEDENCE_COMPARATOR));}// 排序List<PartiallyComparableAdvisorHolder> sorted = PartialOrder.sort(partiallyComparableAdvisors);if (sorted != null) {List<Advisor> result = new ArrayList<>(advisors.size());for (PartiallyComparableAdvisorHolder pcAdvisor : sorted) {result.add(pcAdvisor.getAdvisor());}return result;}else {return super.sortAdvisors(advisors);}
}

针对每一个 advisor,封装一个 PartiallyComparableAdvisorHolder,这是一个 AspectJAwareAdvisorAutoProxyCreator 中的内部类,持有 Advisor 和 Comparator 两个字段,实现了 org.aspectj.util.PartialOrder$PartialComparable 接口,PartialOrder 是一个排序的工具类。

定义的 Comparator 为 AspectJPrecedenceComparator。 

public AspectJPrecedenceComparator() {this.advisorComparator = AnnotationAwareOrderComparator.INSTANCE;
}

定义完 partiallyComparableAdvisors 集合之后,开始对其进行排序。

// PartialOrder
public static <T extends PartialComparable> List<T> sort(List<T> objects) {// lists of size 0 or 1 don't need any sortingif (objects.size() < 2) {return objects;}// ??? we might want to optimize a few other cases of small size// ??? I don't like creating this data structure, but it does give good// ??? separation of concerns.// 我不喜欢创建这样的数据结构,但是它确实很好的将关注点分离了List<SortObject<T>> sortList = new LinkedList<>();for (T object : objects) {addNewPartialComparable(sortList, object);}// System.out.println(sortList);// now we have built our directed graph// use a simple sort algorithm from here// can increase efficiency later// List ret = new ArrayList(objects.size());final int N = objects.size();for (int index = 0; index < N; index++) {// System.out.println(sortList);// System.out.println("-->" + ret);SortObject<T> leastWithNoSmallers = null;// 遍历 sortList,寻找最小的,smallerObjects 数量为0,表明没有比它更小的for (SortObject<T> so: sortList) {if (so.hasNoSmallerObjects()) {if (leastWithNoSmallers == null || so.object.fallbackCompareTo(leastWithNoSmallers.object) < 0) {leastWithNoSmallers = so;}}}// 不存在最小的,返回 nullif (leastWithNoSmallers == null) {return null;}removeFromGraph(sortList, leastWithNoSmallers);// 重新设置 objects 中元素次序objects.set(index, leastWithNoSmallers.object);}return objects;
}

首先创建了一个 SortObject 的集合,命名为 sortList。这里的注释也挺有意思的,作者说他并不喜欢创建这样的数据结构。

遍历 objects,执行 addNewPartialComparable。

private static <T extends PartialComparable> void addNewPartialComparable(List<SortObject<T>> graph, T o) {// 第一次由于 graph 集合是空的,所以只添加// 第二次 graph 不为空,遍历 graph,将之前添加的作为 otherSortObject<T> so = new SortObject<>(o);for (SortObject<T> other : graph) {so.addDirectedLinks(other);}graph.add(so);
}
// SortObject
void addDirectedLinks(SortObject<T> other) {// 当前 SortObject 中 object 和 other 中的 object 比较int cmp = object.compareTo(other.object);if (cmp == 0) {return;}if (cmp > 0) {this.smallerObjects.add(other);other.biggerObjects.add(this);} else {this.biggerObjects.add(other);other.smallerObjects.add(this);}
}
// PartiallyComparableAdvisorHolder 
@Override
public int compareTo(Object obj) {Advisor otherAdvisor = ((PartiallyComparableAdvisorHolder) obj).advisor;// 利用默认的优先级比较器比较两个 advisorreturn this.comparator.compare(this.advisor, otherAdvisor);
}

将 PartiallyComparableAdvisorHolder 传递给 SortObject 中的 object,每个 SortObject 中都存在两个 SortObject 集合,一个命名为 smallerObjects,即存储比当前 object 小的 SortObject 对象,一个命名为 biggerObjects,存储比当前 object 大的 SortObject 对象。

在 PartiallyComparableAdvisorHolder#compareTo 中,利用传入的比较器比较两个 Advisor。

// AspectJPrecedenceComparator
@Override
public int compare(Advisor o1, Advisor o2) {// advisorComparator 为 AnnotationAwareOrderComparator.INSTANCEint advisorPrecedence = this.advisorComparator.compare(o1, o2);// order 相等,且定义在同一个切面类中if (advisorPrecedence == SAME_PRECEDENCE && declaredInSameAspect(o1, o2)) {advisorPrecedence = comparePrecedenceWithinAspect(o1, o2);}return advisorPrecedence;
}

AspectJPrecedenceComparator 又将比较过程委托给其持有的 advisorComparator 去执行,这是一个 AnnotationAwareOrderComparator。

org.springframework.core.OrderComparator 中定义了 compare 方法。

// OrderComparator
@Override
public int compare(@Nullable Object o1, @Nullable Object o2) {return doCompare(o1, o2, null);
}
private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderSourceProvider sourceProvider) {boolean p1 = (o1 instanceof PriorityOrdered);boolean p2 = (o2 instanceof PriorityOrdered);// o1 PriorityOrdered,o2 非 PriorityOrdered// 返回 -1,即 o1 < o2if (p1 && !p2) {return -1;}// o2 PriorityOrdered,o1 非 PriorityOrdered// 返回 1,即 o1 > o2else if (p2 && !p1) {return 1;}// sourceProvider nullint i1 = getOrder(o1, sourceProvider);int i2 = getOrder(o2, sourceProvider);return Integer.compare(i1, i2);
}
// OrderComparator
protected int getOrder(@Nullable Object obj) {if (obj != null) {Integer order = findOrder(obj);if (order != null) {return order;}}return Ordered.LOWEST_PRECEDENCE;
}

获取 order,进行比较。子类 AnnotationAwareOrderComparator 重写了 findOrder 方法。

// AnnotationAwareOrderComparator
@Override
@Nullable
protected Integer findOrder(Object obj) {Integer order = super.findOrder(obj);if (order != null) {return order;}// 注解中获取 orderreturn findOrderFromAnnotation(obj);
}
// 父类 OrderedComparator
@Nullable
protected Integer findOrder(Object obj) {return (obj instanceof Ordered ? ((Ordered) obj).getOrder() : null);
}

先通过 OrderedComparator#findOrder 获取 order,存在就直接返回了,不存在调用 AnnotationAwareOrderComparator#findOrderFromAnnotation 从注解中获取 order。

针对当前情况,传入的两个比较对象都是 Advisor。

配置示例可以参考 AspectJ 对于 AOP 的实现 中 XML 下的配置。

在 AspectJ 基于 XML 的实现下,各标签对应的 Advisor 如下:

标签名称Advisor 类型是否实现 Ordered 接口
advisorDefaultBeanFactoryPointcutAdvisor
aspectAspectJPointcutAdvisor

除此之外,还有扩展时添加的 ExposeInvocationInterceptor.ADVISOR,Advisor 类型为 DefaultPointcutAdvisor。

DefaultBeanFactoryPointcutAdvisor 和 DefaultPointcutAdvisor 都通过继承AbstractPointcutAdvisor,实现了 Ordered 接口。AspectJPointcutAdvisor 自己实现了 Ordered 接口。

先来看 AbstractPointcutAdvisor 实现的 getOrder 方法。

// AbstractPointcutAdvisor
@Override
public int getOrder() {// 获取 orderif (this.order != null) {return this.order;}Advice advice = getAdvice();if (advice instanceof Ordered) {return ((Ordered) advice).getOrder();}return Ordered.LOWEST_PRECEDENCE;
}

XML 中在 advisor 标签中指定的 order 属性,在属性填充时,已经传递给了 AbstractPointcutAdvisor 中 order 属性,此处直接获取,如果 XML 中未指定,取持有的 advice 中的 order,如果都未指定,返回 Ordered.LOWEST_PRECEDENCE,其实是 Integer.MAX_VALUE,即 order 值越大,优先级越低

先来看 ExposeInvocationInterceptor.ADVISOR,未设置 order,所以从持有的 Advice 获取,其持有的 Advice 为 ExposeInvocationInterceptor。

// ExposeInvocationInterceptor
@Override
public int getOrder() {// 最高优先级 -2147483648 + 1 = -2147483647,数值越小,优先级越高return PriorityOrdered.HIGHEST_PRECEDENCE + 1;
}

计算后,返回值为 -2147483647,可以认为优先级最高。

再来看 DefaultBeanFactoryPointcutAdvisor,设置优先级为 2,AbstractPointcutAdvisor#getOrder 时,order 不为 null,直接就返回了。

接着看 AspectJPointcutAdvisor。aspect 标签下的每一个通知对应一个 AspectJPointcutAdvisor,由于 order 定义在 aspect 标签中,所以这两个 AspectJPointcutAdvisor 拥有的 order 一样。此时在 AspectJPrecedenceComparator#compare 方法中,判断切面方法是否在同一个切面类中,在同一个切面类中则调用 AspectJPrecedenceComparator#comparePrecedenceWithinAspect。

// AspectJPrecedenceComparator
private int comparePrecedenceWithinAspect(Advisor advisor1, Advisor advisor2) {// advisor1 和 advisor2 是否存在后置通知boolean oneOrOtherIsAfterAdvice =(AspectJAopUtils.isAfterAdvice(advisor1) || AspectJAopUtils.isAfterAdvice(advisor2));// declarationOrder 值相减  3-1int adviceDeclarationOrderDelta = getAspectDeclarationOrder(advisor1) - getAspectDeclarationOrder(advisor2);// 存在后置通知if (oneOrOtherIsAfterAdvice) {// the advice declared last has higher precedence// 后定义的 advice 拥有更高的优先级if (adviceDeclarationOrderDelta < 0) {// advice1 was declared before advice2// so advice1 has lower precedencereturn LOWER_PRECEDENCE; // 1}else if (adviceDeclarationOrderDelta == 0) {return SAME_PRECEDENCE; //0}else {return HIGHER_PRECEDENCE; // -1}}else {// the advice declared first has higher precedence// 不存在后置通知,先定义的拥有更高的优先级if (adviceDeclarationOrderDelta < 0) {// advice1 was declared before advice2// so advice1 has higher precedencereturn HIGHER_PRECEDENCE; // -1}else if (adviceDeclarationOrderDelta == 0) {return SAME_PRECEDENCE;  // 0}else {return LOWER_PRECEDENCE; // 1}}
}
// AspectJAopUtils
public static boolean isAfterAdvice(Advisor anAdvisor) {AspectJPrecedenceInformation precedenceInfo = getAspectJPrecedenceInformationFor(anAdvisor);// 判断是否是后置通知if (precedenceInfo != null) {return precedenceInfo.isAfterAdvice();}return (anAdvisor.getAdvice() instanceof AfterAdvice);
}
@Nullable
public static AspectJPrecedenceInformation getAspectJPrecedenceInformationFor(Advisor anAdvisor) {// AspectJPrecedenceInformation 子类 InstantiationModelAwarePointcutAdvisorImpl 和 AbstractAspectJAdvice// InstantiationModelAwarePointcutAdvisorImpl 是 AspectJ 注解模式下创建的 Advisorif (anAdvisor instanceof AspectJPrecedenceInformation) {return (AspectJPrecedenceInformation) anAdvisor;}// AspectJPointcutAdvisor 获取持有 AdviceAdvice advice = anAdvisor.getAdvice();if (advice instanceof AspectJPrecedenceInformation) {return (AspectJPrecedenceInformation) advice;}return null;
}// AspectJPrecedenceComparator
private int getAspectDeclarationOrder(Advisor advisor) {AspectJPrecedenceInformation precedenceInfo = AspectJAopUtils.getAspectJPrecedenceInformationFor(advisor);return (precedenceInfo != null ? precedenceInfo.getDeclarationOrder() : 0);
}

Advisor 位于同一个 aspect 标签下,order 相等,此时通过 AbstractAspectJAdvice 中 declarationOrder 来进行比较。在定义 adviceDefinition 时添加 declarationOrder,其实就是通知子节点顺序。

xml 下节点顺序:

<aop:aspect>0 [注释]1 <aop:before>2 [注释]3 <aop:after>4 [注释]
</aop:aspect>

所以 <aop:before> 的 declarationOrder 为 1,<aop:after> 的 declarationOrder 为 3。

接着,判断是否存在后置通知,存在后置通知时,后定义的 Advisor 优先级更高,不存在后置通知时,先定义的 Advisor 优先级更高

示例中存在后置通知,按照规则,<aop:after> 标签对应的 AspectJPointcutAdvisor 优先级高。可以认为 <aop:after> 对应的 AspectJPointcutAdvisor 小于  <aop:before> 对应的 AspectJPointcutAdvisor。

确定完比较的规则之后,再回到 SortObject#addDirectedLinks 方法中,填充 smallerObjects 和 biggerObjects。

以示例中得到的 eligibleAdvisors 为例,演示下这个过程。

实际 objects 中各元素为 PartiallyComparableAdvisorHolder,此处为方便说明,以持有的 Advisor 指代。

objects[0] 为 ExposeInvocationInterceptor.ADVISOR,order=-2147483647
objects[1] 为 DefaultBeanFactoryPointcutAdvisor,order=2
objects[2] 为 AspectJPointcutAdvisor,order=1,declarationOrder=1
objects[3] 为 AspectJPointcutAdvisor,order=1,declarationOrder=3

第一次,graph 为空的,遇见 objects[0],封装为 SortObject 之后加入 graph。

第二次,graph[0] = objects[0],当前的 o 为 objects[1],封装 SortObject,由于之后会加入 graph,此处提前将其表示为 graph[1] = objects[1]。

soothercmpsmallerObjects/biggerObjects
graph[1]graph[0]1

graph[1].smallerObjects = graph[0]

graph[0].biggerObjects = graph[1]

第三次,graph 中已经有两个元素了,graph[0] = objects[0],graph[1] = objects[1],待加入的 graph[2] = objects[2]。循环 graph[0] 和 graph[1],用 graph[2] 分别和 graph[0]、graph[1] 作比较。

soothercmpsmallerObjects/biggerObjects
graph[2]graph[0]1

graph[2].smallerObjects = graph[0]

graph[0].biggerObjects = graph[1],graph[2]

graph[1]-1

graph[1].smallerObjects = graph[0],graph[2]

graph[2].biggerObjects = graph[1]

第四次,graph 中已经有三个元素了,graph[0] = objects[0],graph[1] = objects[1],graph[2] = objects[2],待加入的 graph[3] = objects[3]。循环 graph[0] 、graph[1] 和 graph[2],用 graph[3] 分别和 graph[0]、graph[1]、graph[2] 作比较。

soothercmpsmallerObjects/biggerObjects
graph[3]graph[0]1

graph[3].smallerObjects = graph[0]

graph[0].biggerObjects = graph[1],graph[2],graph[3]

graph[1]-1

graph[1].smallerObjects = graph[0],graph[2],graph[3]

graph[3].biggerObjects = graph[1]

graph[2]-1

graph[2].smallerObjects = graph[0],graph[3]

graph[3].biggerObjects = graph[1],graph[2]

填充完 smallerObjects/biggerObjects 之后,又回到 PartialOrder#sort 方法中,执行 for 循环,在 for 循环中,再遍历 sortList,寻找最小的,smallerObjects 数量为0,表明没有比它更小的。

第一次找到 graph[0],它的 smallerObjects 数量为 0,继而执行 removeFromGraph 方法。

// PartialOrder
private static <T extends PartialComparable> void removeFromGraph(List<SortObject<T>> graph, SortObject<T> o) {for (Iterator<SortObject<T>> i = graph.iterator(); i.hasNext();) {SortObject<T> other = i.next();// 移除if (o == other) {i.remove();}// ??? could use this to build up a new queue of objects with no// ??? smaller ones// 从每一个 other.smallerObjects 里面移除 oother.removeSmallerObject(o);}
}
boolean removeSmallerObject(SortObject<T> o) {smallerObjects.remove(o);return hasNoSmallerObjects();
}

确定最小的 o 之后,迭代 graph,从 graph 中移除 o,之后从其余的 other 的 smallerObjects 中移除这个最小的。

完成之后在 PartialOrder#sort 进行下一轮循环。

示例如下:

indexleastWithNoSmallers移除后 graphsmallerObjects
0graph[0]graph[1],graph[2],graph[3]

graph[1].smallerObjects = graph[2],graph[3]

graph[2].smallerObjects = graph[3]

graph[3].smallerObjects 移除后空了

1graph[3]graph[1],graph[2]

graph[1].smallerObjects = graph[2]

graph[2].smallerObjects 移除后空了

2graph[2]graph[1]graph[1].smallerObjects 移除后空了
3graph[1]空了空了

这样,就按优先级顺序,重置了 objects 中各元素的顺序。

再回到 AspectJAwareAdvisorAutoProxyCreator#sortAdvisors 方法中,排序之前将每一个 Advisor 封装成 PartiallyComparableAdvisorHolder,此时需要提取出 Advisor,加入 result 后作为结果返回。

实际上,这是拓扑排序的一种应用,存在的 smallerObjects 表示依赖的活动(activity),smallerObjects 数量为 0,表明不依赖任何活动。绘制出的 AOV 网如下:

相关文章:

AspectJ 下 Advisor 的排序过程

在 AbstractAdvisorAutoProxyCreator#findEligibleAdvisors 方法中&#xff0c;找到 BeanFactory 中所有的 Advisor 后&#xff0c;针对当前 beanClass 进行过滤&#xff0c;筛选出符合当前 beanClass 的 Advisor&#xff0c;称之为 eligibleAdvisors。接着对 eligibleAdvisors…...

Unity 高度可扩展的技能与多 Buff 框架详解

一、框架设计 1.1 核心思想 组件化设计: 将技能和 Buff 抽象为可复用的组件&#xff0c;通过组合不同的组件实现复杂的效果。 数据驱动: 使用 ScriptableObject 或 JSON 等数据格式定义技能和 Buff 的属性&#xff0c;方便配置和修改。 事件驱动: 利用 Unity 的事件系统或自…...

机器学习中过拟合和欠拟合问题处理方法总结

目录 一、背景二、过拟合(Overfitting)2.1 基本概念2.2 过拟合4个最主要的特征2.3 防止过拟合的11个有效方法 三、欠拟合&#xff08;Underfitting&#xff09;3.1 基本概念3.2 欠拟合的4个特征3.3 防止欠拟合的11个有效方法 四、总结五、参考资料 一、背景 在机器学习模型训练…...

新一代高性能无线传输模块M-GATEWAY3

M-GATEWAY3是M3系列的通用接口模块&#xff0c;用于接收各种总线信号并将它们集成到一个系统中。该模块通过标准化传输协议XCPonETH进行输出&#xff0c;确保为各种测量应用提供无损信号。此外&#xff0c;M-GATEWAY3支持通过热点、ETH-PC或USB-C传输数据。借助M-GATEWAY3&…...

一、通义灵码插件保姆级教学-IDEA(安装篇)

JetBrains IDEA 中安装教学 第一步&#xff1a;事先准备 支持系统&#xff1a;Windows 7 ~ Windows 11、Linux、macOS&#xff1b;下载并安装兼容的 JetBrains IDEA 2020.3 及以上版本&#xff1b; 第二步&#xff1a;在 IntelliJ IDEA 中安装通义灵码 方法 1&#xff1a;通…...

【leetcode 32】1047. 删除字符串中的所有相邻重复项

Java中的queue、deque、ArrayDeque的区别 使用 Deque 作为堆栈(ArrayDeque是双端队列&#xff0c;当仅使用栈操作时push/pop&#xff0c;就是栈) class Solution {public String removeDuplicates(String s) {//使用 Deque 作为堆栈(ArrayDeque是双端队列&#xff0c;当仅使用栈…...

ubuntu 安装vnc之后,本地黑屏,vnc正常

ubuntu 安装vnc之后,本地黑屏,vnc正常 在Ubuntu系统中安装VNC服务器&#xff08;如TightVNC或RealVNC&#xff09;后&#xff0c;如果遇到连接时本地屏幕变黑的情况&#xff0c;可能是由于几种不同的配置或兼容性问题。以下是一些解决步骤&#xff0c;可以帮助你解决这个问题&…...

【实战AI】利用deepseek 在mac本地部署搭建个人知识库

之前的文章中实现了本地通过ollma 部署deepseek R1&#xff1a;14b 模型&#xff0c;这里我想继续实现个人知识库&#xff0c;方便自己文档&#xff0c;数据的检索&#xff1b; 下载anythingLLM 地址&#xff1a; https://anythingllm.com/desktop 下载安装即可&#xff1b…...

ASP.NET Core的贫血模型与充血模型

目录 概念 需求 贫血模型 充血模型 总结 概念 贫血模型&#xff1a;一个类中只有属性或者成员变量&#xff0c;没有方法。充血模型&#xff1a;一个类中既有属性、成员变量&#xff0c;也有方法。 需求 定义一个类保存用户的用户名、密码、积分&#xff1b;用户必须具有…...

java基础6(黑马)

一、static 1.static修饰成员变量 static&#xff1a;叫静态&#xff0c;可以修饰成员变量、成员方法。 成员变量按照有无static&#xff0c;分两种。 类变量&#xff1a;有static修饰&#xff0c;属于类&#xff0c;在计算机中只有一份&#xff0c;会被类的全部对象共享。…...

LabVIEW外延片光致发光(PL)扫描系统

本文介绍了基于LabVIEW的PL扫描系统的开发过程&#xff0c;包括硬件选型&#xff08;如ECOPIA激光器、EPP2000光谱仪&#xff09;、软件架构&#xff08;多线程控制、动态显示&#xff09;、环境适配&#xff08;温湿度控制、防震&#xff09;等关键设计。结合工业需求&#xf…...

安装Jenkins

Linux中安装jenkins 采用RPM安装包方式&#xff1a;Jenkins安装包下载地址 wget https://pkg.jenkins.io/redhat-stable/jenkins-2.190.1-1.1.noarch.rpm执行安装 rpm -ivh jenkins-2.190.1-1.1.noarch.rpm修改配置文件&#xff1a; vi /etc/sysconfig/jenkins修改内容&…...

开发中常用的快捷键

开发中常用的快捷键 浏览器开发工具ideavscode 浏览器 清除浏览器的缓存&#xff1a;ctrl shift delete 开发工具 idea 前端JS代码格式化&#xff1a;ctrl alt L vscode...

前端如何判断浏览器 AdBlock/AdBlock Plus(最新版)广告屏蔽插件已开启拦截

2个月前AdBlock/AdBlock Plus疑似升级了一次 因为自己主要负责面对海外的用户项目&#xff0c;发现以前的检测AdBlock/AdBlock Plus开启状态方法已失效了&#xff0c;于是专门研究了一下。并尝试了很多方法。 已失效的老方法 // 定义一个检测 AdBlock 的函数 function chec…...

ASP.NET Core 如何使用 C# 向端点发出 POST 请求

使用 C#&#xff0c;将 JSON POST 到 REST API 端点&#xff1b;如何从 REST API 接收 JSON 数据。 本文需要 ASP .NET Core&#xff0c;并兼容 .NET Core 3.1、.NET 6和.NET 8。 要从端点获取数据&#xff0c;请参阅本文。 使用 . 将 JSON 数据发布到端点非常容易HttpClien…...

更新无忧:用 Docker 数据卷确保 Open WebUI 数据持久化

在使用 Docker 部署 Open WebUI 时&#xff0c;如何在更新容器的同时确保数据不丢失&#xff0c;始终是工程师们关注的焦点。每次拉取新版镜像、停止并重启容器时&#xff0c;如果没有正确挂载数据卷&#xff0c;配置和数据库数据极易流失&#xff0c;给生产环境带来不必要的麻…...

Ubuntu 下 nginx-1.24.0 源码分析 - ngx_atomic_cmp_set 函数

目录 修正 执行 ./configure 命令时&#xff0c;输出&#xff1a; checking for OS Linux 6.8.0-52-generic x86_64 checking for C compiler ... found using GNU C compiler gcc version: 11.4.0 (Ubuntu 11.4.0-1ubuntu1~22.04) 所以当前环境是 x86_64 于是在 src…...

为什么我用Python控制仪器比C#慢很多?如何优化性能?

在自动化测试、实验室仪器控制等领域&#xff0c;Python、C# 和 C 是常见的编程语言选择。最近&#xff0c;我在使用 Python 控制仪器时&#xff0c;发现其交互速度明显比 C# 慢很多。这让我感到困惑&#xff0c;毕竟 Python 以其简洁和高效著称&#xff0c;为什么会出现这种情…...

Linux虚拟机克隆

克隆 从现有虚拟机(关机状态)克隆出新虚拟机&#xff0c;右键选择管理>克隆&#xff1a; 选择完整克隆 设置虚拟机名称及存储位置 开机修改系统相关配置 注意: 使用root 用户。 修改vim /etc/sysconfig/network-scripts/ifcfg-ens33 ,修改IP 地址 vim /etc/sysconfig…...

开发完的小程序如何分包

好几次了&#xff0c;终于想起来写个笔记记一下 我最开始并不会给小程序分包&#xff0c;然后我就各种搜&#xff0c;发现讲的基本上都是开发之前的小程序分包&#xff0c;可是我都开发完要发布了&#xff0c;提示我说主包太大需要分包&#xff0c;所以我就不会了。。。 好了…...

气体控制器联动风机,检测到环境出现异常时自动打开风机进行排风;

一、功能&#xff1a;检测到环境出现异常时自动打开风机进行排风&#xff1b; 二、设备&#xff1a; 1.气体控制器主机&#xff1a;温湿度&#xff0c;TVOC等探头的主机&#xff0c;可上报数据&#xff0c;探头监测到异常时&#xff0c;主机会监测到异常可联动风机或声光报警…...

攻防世界33 catcat-new【文件包含/flask_session伪造】

题目&#xff1a; 点击一只猫猫&#xff1a; 看这个url像是文件包含漏洞&#xff0c;试试 dirsearch扫出来/admin&#xff0c;访问也没成功&#xff08;--delay 0.1 -t 5&#xff09; 会的那几招全用不了了哈哈&#xff0c;那就继续看答案 先总结几个知识点 1./etc/passwd&am…...

让文物“活”起来,以3D数字化技术传承文物历史文化!

文物&#xff0c;作为不可再生的宝贵资源&#xff0c;其任何毁损都是无法逆转的损失。然而&#xff0c;当前文物保护与修复领域仍大量依赖传统技术&#xff0c;同时&#xff0c;文物管理机构和专业团队的力量相对薄弱&#xff0c;亟需引入数字化管理手段以应对挑战。 积木易搭…...

Java+vue前后端分离项目集群部署

一、项目概述 假设我们有一个前后端分离的项目&#xff0c;前端使用React或Vue框架&#xff0c;后端使用Spring Boot或Node.js。我们将分别部署前端和后端到集群环境中。 二、准备工作 1. 代码准备&#xff1a;确保前端和后端代码已经开发完成&#xff0c;并通过本地测试。 2…...

【算法解析】(2)分治算法:归并排序和快速排序

1. 分治算法(Divide and Conquer) 分治算法的核心思想是将一个大问题分解为多个小问题,分别解决这些小问题,然后将小问题的解合并起来得到大问题的解。 2. 算法步骤 分治算法通常包含以下三个步骤: 分解(Divide):将原问题分解为若干个规模较小、相互独立、与原问题…...

Qt文本处理【正则表达式】示例详解:【QRegularExpression】

在 Qt 中&#xff0c;正则表达式是处理文本的强大工具&#xff0c;它能够帮助我们匹配、搜索和替换特定的字符串模式。自 Qt 5 起&#xff0c;QRegularExpression 类提供了对 ECMAScript 标准的正则表达式支持&#xff0c;这使得它在处理各种复杂的字符串任务时变得更加高效和灵…...

在 Navicat 17 中扩展 PostgreSQL 数据类型 - 枚举类型

枚举类型 在 Navicat Premium 17 中创建 PostgreSQL 的自定义数据类型的系列中&#xff0c;我们已经探索了多个主题。在 第 1 部分 &#xff0c;我们学习了如何为 免费 DVD 租赁数据库 创建自定义域。上周&#xff0c;我们创建了一个复合类型&#xff0c;用于从用户定义的函数…...

Spring Boot + MyBatis Field ‘xxx‘ doesn‘t have a default value 问题排查与解决

目录 1. 问题所示2. 原理分析3. 解决方法1. 问题所示 执行代码的时候,出现某个字段无法添加 ### Error updating database. Cause: java.sql.SQLException: Field e_f_id doesnt have a default value ### The error may exist in cn...

python基础入门:6.2JSON与CSV数据处理

Python数据处理实战&#xff1a;JSON与CSV高效转换指南 # 天气数据转换示例 import json import csv from datetime import datetimedef process_weather_data(json_file, csv_file):"""将天气JSON数据转换为CSV格式"""with open(json_file, r,…...

尚硅谷课程【笔记】——大数据之Zookeeper【二】

课程视频&#xff1a;【尚硅谷Zookeeper教程】 四、Zookeeper实战 4.1分布式安装部署 1. 集群规划 在Hadoop102、Hadoop103和Hadoop104三个节点上部署Zookeeper 2. 解压安装 1&#xff09;解压Zookeeper.tar.gz到指定目录 tar -zxvf zookeeper-3.7.2.tar.gz -C /opt/mod…...

机器学习常用包matplotlib篇(一)简单图像绘制

前言 Matplotlib 是支持 Python 语言的开源绘图库&#xff0c;简单且完善。 一、环境配置 1.环境设置 在 Notebook 环境绘图时&#xff0c;需先运行 %matplotlib inline 命令&#xff0c;将绘制图形嵌入当前页面。在桌面环境绘图&#xff0c;无需上述命令&#xff0c;而是在…...

JUnit断言方法详解与实战

在Java开发中&#xff0c;JUnit是一个不可或缺的单元测试框架&#xff0c;而org.junit.Assert类中的断言方法则是JUnit的核心功能之一。通过这些方法&#xff0c;我们可以方便地验证代码的正确性。本文将详细介绍一些常用的断言方法&#xff0c;并通过实例展示它们的使用。 一、…...

npm运行Vue项目报错 error:0308010c:digital envelope routines::unsupported

大家好&#xff0c;我是 程序员码递夫。 问题 VSCode 运行Vue项目&#xff0c;提示错误&#xff1a; building 2/2 modules 0 activeError: error:0308010c:digital envelope routines::unsupported 解决方法 原因是 npm 高版本(大于17)&#xff0c;对ssl的处理做了改进&…...

C语言操作符详解

引言 C语言作为一种强大而灵活的编程语言&#xff0c;操作符是其重要组成部分。操作符用于执行各种运算&#xff0c;如算术运算、逻辑运算、比较运算等。深入理解C语言操作符&#xff0c;能帮助开发者编写出高效、准确的代码。 算术操作符 基本算术操作符 - &#xff08;加法…...

Lucene 中的并发错误:如何修复乐观并发失败

作者&#xff1a;来着 Elastic Benjamin Trent 及 Ao Li 感谢 CMU PASTA 实验室开发的确定性并发测试框架 Fray&#xff0c;我们找到了一个棘手的 Lucene 漏洞并将其修复。 是的&#xff0c;另一个修复错误博客。但这个故事有一个转折&#xff0c;一位开源英雄突然出现并拯救了…...

Oracle的学习心得和知识总结(三十三)|Oracle数据库数据库的SQL ID的底层计算原理分析

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《Oracle Database SQL Language Reference》 2、参考书籍&#xff1a;《PostgreSQL中文手册》 3、EDB Postgres Advanced Server User Gui…...

C# OpenCV机器视觉:智能水果采摘

在一个风景如画的小镇边上&#xff0c;有一座阿强家祖传的果园。每到水果成熟的季节&#xff0c;果园里硕果累累&#xff0c;红彤彤的苹果、黄澄澄的梨子、紫莹莹的葡萄&#xff0c;散发着诱人的香气。然而&#xff0c;这丰收的喜悦却总被一件烦心事笼罩 —— 摘水果。 “哎呀…...

逻辑回归不能解决非线性问题,而svm可以解决

逻辑回归和支持向量机&#xff08;SVM&#xff09;是两种常用的分类算法&#xff0c;它们在处理数据时有一些不同的特点&#xff0c;特别是在面对非线性问题时。 1. 逻辑回归 逻辑回归本质上是一个线性分类模型。它的目的是寻找一个最适合数据的直线&#xff08;或超平面&…...

celery + redis - 入门

文章目录 一、基本使用编写任务启动服务创建生产者获取状态和结果二、多目录结构异步执行编写服务启动服务调用服务获取结果https://www.bilibili.com/video/BV1jg4y13718 https://www.cnblogs.com/pyedu/p/12461819.html 一、基本使用 编写任务 celery_task.py import cel…...

SAP-ABAP:在LOOP循环中 ASSIGNING FIELD-SYMBOL的使用代码详解

在ABAP中&#xff0c;ASSIGNING FIELD-SYMBOL的作用是直接引用内表行的数据&#xff0c;避免不必要的数据复制&#xff0c;从而提升性能和代码效率。以下是其核心作用和优势&#xff1a; 基本语法 LOOP AT itab ASSIGNING FIELD-SYMBOL(<fs>)." 处理逻辑 ENDLOOP.i…...

SpringBoot启动流程简略版

启动入口 (main 方法) ↓ SpringApplication 初始化 ↓ 加载配置 (application.properties/yml) ↓ 创建 ApplicationContext ↓ 刷新 ApplicationContext ↓ - 加载 Bean 定义 - 执行自动配置 - 实例化 Bean - 依赖注入 - 调用初…...

Python:凯撒密码

题目内容&#xff1a; 凯撒密码是古罗马恺撒大帝用来对军事情报进行加密的算法&#xff0c;它采用了替换方法对信息中的每一个英文字符循环替换为字母表序列该字符后面第三个字符&#xff0c;对应关系如下&#xff1a; 原文&#xff1a;A B C D E F G H I J K L M N O P Q R …...

element-plus 解决el-dialog背后的页面滚动问题,及其内容有下拉框出现错位问题

这个问题通常是因为 el‑dialog 默认会锁定 body 的滚动&#xff08;通过给 body 添加隐藏滚动条的样式&#xff09;&#xff0c;从而导致页面在打开对话框时跳转到顶部。解决方法是在使用 el‑dialog 时禁用锁定滚动功能。 <el-dialogv-model"dialogVisible":lo…...

Android和DLT日志系统

1 Linux Android日志系统 1.1 内核logger机制 drivers/staging/android/logger.c static size_t logger_offset( struct logger_log *log, size_t n) { return n & (log->size - 1); } 写的off存在logger_log中&#xff08;即内核内存buffer&#xff09;&am…...

Ubuntu 安装 NVIDIA 驱动实操指南(含卸载)

本文将详细介绍如何在Ubuntu上安装和配置NVIDIA显卡驱动。以下是一步步的操作流程&#xff0c;包括禁用开源驱动的步骤。 步骤 1&#xff1a;安装依赖 首先&#xff0c;确保系统中已安装gcc和make&#xff0c;这些是编译驱动所需的依赖。 sudo apt update sudo apt install …...

在postman中设置环境变量和全局变量以及五大常用响应体断言

一、什么是环境变量和全局变量 环境变量&#xff08;Environment Variables&#xff09;和全局变量&#xff08;Global Variables&#xff09;是 Postman 中用于存储和管理数据的两种变量类型&#xff0c;它们可以提高 API 测试的灵活性和可维护性。 1、 环境变量&#xff08…...

32单片机学习记录1之GPIO

32单片机学习记录1之GPIO 前置 GPIO口在单片机中扮演着什么角色&#xff1f; 在单片机中&#xff0c;GPIO口&#xff08;General Purpose Input/Output&#xff09; 是一种通用输入/输出接口&#xff0c;扮演着连接单片机与外部设备的桥梁角色。具体来说&#xff0c;它在单片…...

负载测试和压力测试的原理分别是什么

负载测试和压力测试是性能测试的两种主要类型&#xff0c;它们的原理和应用场景有所不同。 负载测试&#xff08;Load Testing&#xff09; 原理&#xff1a; 负载测试通过模拟实际用户行为&#xff0c;逐步增加系统负载&#xff0c;观察系统在不同负载下的表现。目的是评估系…...

openAI官方prompt技巧(二)

1. 赋予 ChatGPT 角色 为 ChatGPT 指定一个角色&#xff0c;让其从特定的身份或视角回答问题。这有助于生成针对特定受众或场景的定制化回答。 例如&#xff1a; 你是一名数据分析师&#xff0c;负责我们的市场营销团队。请总结上个季度的营销活动表现&#xff0c;并强调与未…...

javaEE-11.javaScript入门

目录 一.什么是javaScript 二.快速实现 三.JS引入方式 1.行内引入: 2.内部引入: 3.外部引入: 四.基础语法 1.变量 变量命名规则: 2.数据类型 3.运算符 五.JS对象 1.数组 创建数组: 2.操作数组 3.函数 函数注意事项: 函数参数: 4.对象 1.使用字面量 创建对象:…...