第二十章 Spring之假如让你来写AOP——Aspect(切面)篇

Spring源码阅读目录

第一部分——IOC篇

第一章 Spring之最熟悉的陌生人——IOC
第二章 Spring之假如让你来写IOC容器——加载资源篇
第三章 Spring之假如让你来写IOC容器——解析配置文件篇
第四章 Spring之假如让你来写IOC容器——XML配置文件篇
第五章 Spring之假如让你来写IOC容器——BeanFactory和FactoryBean
第六章 Spring之假如让你来写IOC容器——Scope和属性填充
第七章 Spring之假如让你来写IOC容器——属性填充特别篇:SpEL表达式
第八章 Spring之假如让你来写IOC容器——拓展篇
第九章 Spring之源码阅读——环境搭建篇
第十章 Spring之源码阅读——IOC篇

第二部分——AOP篇

第十一章 Spring之不太熟的熟人——AOP
第十二章 Spring之不得不了解的内容——概念篇
第十三章 Spring之假如让你来写AOP——AOP联盟篇
第十四章 Spring之假如让你来写AOP——雏形篇
第十五章 Spring之假如让你来写AOP——Joinpoint(连接点)篇
第十六章 Spring之假如让你来写AOP——Pointcut(切点)篇
第十七章 Spring之假如让你来写AOP——Advice(通知)上篇
第十八章 Spring之假如让你来写AOP——Advice(通知)下篇
第十九章 Spring之假如让你来写AOP——番外篇:Spring早期设计
第二十章 Spring之假如让你来写AOP——Aspect(切面)篇
第二十一章 Spring之假如让你来写AOP——Weaver(织入器)篇
第二十二章 Spring之假如让你来写AOP——Target Object(目标对象)篇
第二十三章 Spring之假如让你来写AOP——融入IOC容器篇
第二十四章 Spring之源码阅读——AOP篇


文章目录

  • Spring源码阅读目录
    • 第一部分——IOC篇
    • 第二部分——AOP篇
  • 前言
  • 尝试动手写IOC容器
      • 第二十版 Aspect(切面)篇
        • 切点、通知的良媒——Advisor
        • 切面真正的管理者——Advised
  • 总结


前言

    对于Spring一直都是既熟悉又陌生,说对它熟悉吧,平时用用没啥问题,但面试的时候被问的一脸懵逼,就很尴尬,都不好意思在简历上写着熟悉Spring了
在这里插入图片描述

    所以决定花点时间研究研究Spring的源码。主要参考的书籍是:《Spring源码深度解析(第2版)》、《Spring揭秘》、《Spring技术内幕:深入解析Spring架构与设计原理(第2版)》


    书接上上回,在上上篇 第十八章 Spring之假如让你来写AOP——Advice(通知)下篇 中,A君 废了老大的劲,终于实现了 Advice(通知) 的内容,虽然道路曲折,但是前途是光明的。接下来看看 A君 会有什么骚操作吧

尝试动手写IOC容器

    出场人物:A君(苦逼的开发)、老大(项目经理)

    背景:老大要求A君在一周内开发个简单的 IOC容器

    前情提要: A君 已经完成 Advice(通知) 的内容 。。。

第二十版 Aspect(切面)篇

    “可以啊,A君Advice(通知) 弄的出人意表,比我想象中的还好” 老大 不吝夸赞道

    “开玩笑,拼了老命才弄出来的。” A君 心道

    “接下来,我们来讨论下 Aspect(切面)Aspect(切面) 说到底,就是用来管理 Advice(通知) 的。这部分内容你自由发挥把,别忘了,面向接口编程,还有抽象类。” 老大 今天难得没说什么,只是这“自由发挥”的要求也太抽象了

切点、通知的良媒——Advisor

    开完会,A君 独自坐在位置上思考:Aspect(切面) 要如何实现?直接定义一个接口,让它拥有CRUD的方法吗?A君 随手写了个例子,如下:

import com.hqd.test.aop.Advice;
import com.hqd.test.aop.Pointcut;import java.util.List;public class Advisor {private List<Advice> adviceList;private List<Pointcut> pointcuts;public void addAdvice(Advice advice) {this.adviceList.add(advice);}public void addPointcut(Pointcut pointcut) {this.pointcuts.add(pointcut);}
}

额,不太对头,这无法知道 Advice(通知)Pointcut(切点) 的对应关系。通过Map封装?这又不好管理了。算了,A君 索性把 Advice(通知)Pointcut(切点) 包装成一个类,这样方便管理。A君 定个 Advisor 接口,用以维护 Advice(通知)Pointcut(切点) 的对应关系。代码如下:

import com.hqd.ch03.v20.aopalliance.aop.Advice;/*** 维护切点和通知的映射关系接口*/
public interface Advisor {Advice EMPTY_ADVICE = new Advice() {};Advice getAdvice();
}

Advisor 是个顶级接口,本身只有最通用的功能。还需要子接口去拓展其功能,例如:切点信息。额,那为什么不直接在 Advisor 接口中一起定义。嘿,因为还有个 引介增强,它是以类为维度进行增强的,并不需要切点。PointcutAdvisor 接口如下:

/*** 切点和通知对应关系*/
public interface PointcutAdvisor extends Advisor {Pointcut getPointcut();
}

引介增强 也需要维护映射关系。它是的切点比较特殊,是 ClassFilter,而不是 Pointcut。接口如下:

/*** 引介增强映射关系*/
public interface IntroductionAdvisor extends Advisor, IntroductionInfo {ClassFilter getClassFilter();/*** 检验接口** @throws IllegalArgumentException*/void validateInterfaces() throws IllegalArgumentException;
}

Advisor 本身的定义并不复杂,所以其实现也没有什么奇特的地方。虽然简单,可是 老大 言犹在耳。A君 稍微思索了下:Advisor 的实现不就是存个通知和切点就完事了,还有哪里需要提取抽象类的?A君 转念一想:现在框架切点匹配有一大堆实现,让用户一顿 new Advisor(new Advice(),new Pointcut()) 或者xml中配置一堆bean,再ref,好像也不是事。说好的,简单呢?说好的,开箱即用呢?切点的问题解决了。那么通知也需要这么处理吗?应该没必要,如果是通过实现接口的通知,那么压根就没法处理,总不能特地定义个子类,只是为了换个类型吧。至于 AspectJ 的通知,它另有去处。嘿嘿。那,这些不同实现的 Advisor,有什么公共点?要说公共,倒是也有一点,那就是顺序。要知道 Advisor 是单个切点和通知的管理者,通知是有顺序的,那么管理者也应该是有序的,而且这个顺序还不能乱来,得用通知的才行。这么一想,整个 Advisor 的轮廓就出来了。“果然,还是得深思熟虑呀!不然又得挨叼了。” A君 感叹

    思量完毕,A君 先提取一个抽象类,不干别的,把排序提取出来就行了。AbstractPointcutAdvisor 代码如下:

import com.hqd.ch03.v20.aop.PointcutAdvisor;
import com.hqd.ch03.v20.aopalliance.aop.Advice;
import com.hqd.ch03.v20.core.Ordered;public abstract class AbstractPointcutAdvisor implements PointcutAdvisor, Ordered {private Integer order;@Overridepublic int getOrder() {if (this.order != null) {return this.order;}Advice advice = getAdvice();if (advice instanceof Ordered) {return ((Ordered) advice).getOrder();}return Ordered.LOWEST_PRECEDENCE;}public void setOrder(int order) {this.order = order;}}

都做到这一步了,那索性把通知也提取成抽象类算了。A君 在定义个 AbstractGenericPointcutAdvisor 类。代码如下:

import com.hqd.ch03.v20.aopalliance.aop.Advice;public abstract class AbstractGenericPointcutAdvisor extends AbstractPointcutAdvisor {private Advice advice = EMPTY_ADVICE;@Overridepublic Advice getAdvice() {return this.advice;}public void setAdvice(Advice advice) {this.advice = advice;}
}

好啦,接口和抽象类都搞定了,接下来要弄的事,就是把不同切点匹配映射到对应的 Advisor 实现就行了。俗话说:柿子拣软的捏。A君 先拿 NameMatchMethodPointcut 开刀。只需要把 NameMatchMethodPointcut 创建出来就行了。** NameMatchMethodPointcutAdvisor** 代码如下:

import com.hqd.ch03.v20.aop.ClassFilter;
import com.hqd.ch03.v20.aop.Pointcut;
import com.hqd.ch03.v20.aopalliance.aop.Advice;public class NameMatchMethodPointcutAdvisor extends AbstractGenericPointcutAdvisor {private final NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();public NameMatchMethodPointcutAdvisor() {}public NameMatchMethodPointcutAdvisor(Advice advice) {setAdvice(advice);}public void setClassFilter(ClassFilter classFilter) {this.pointcut.setClassFilter(classFilter);}public NameMatchMethodPointcut addMethodName(String name) {return this.pointcut.addMethodName(name);}public Pointcut getPointcut() {return this.pointcut;}
}

再来个切点表达式的,AspectJExpressionPointcutAdvisor 代码如下:

import com.hqd.ch03.v20.aop.Pointcut;
import com.hqd.ch03.v20.aop.support.AbstractGenericPointcutAdvisor;public class AspectJExpressionPointcutAdvisor extends AbstractGenericPointcutAdvisor {private final AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();public String getExpression() {return this.pointcut.getExpression();}public void setExpression(String expression) {this.pointcut.setExpression(expression);}@Overridepublic Pointcut getPointcut() {return this.pointcut;}
}

其他切点匹配都是类似的,这里 A君 就不再一一赘述了。NameMatchMethodPointcutAdvisor 这一类的 Advisor 都是提供给用户,方便用户使用的。但也不因此而忘记本心——自由搭配。A君 再定义一个 DefaultPointcutAdvisor 类,这个类不做任何限制,只要实现了 AdvicePointcut 接口就行。代码如下:

import com.hqd.ch03.v20.aop.Pointcut;
import com.hqd.ch03.v20.aopalliance.aop.Advice;public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor {private Pointcut pointcut = Pointcut.TRUE;public DefaultPointcutAdvisor() {}public DefaultPointcutAdvisor(Advice advice) {this(Pointcut.TRUE, advice);}public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {this.pointcut = pointcut;setAdvice(advice);}@Overridepublic Pointcut getPointcut() {return this.pointcut;}public void setPointcut(Pointcut pointcut) {this.pointcut = (pointcut != null ? pointcut : Pointcut.TRUE);}
}

上诉提到的 Advisor 实现,都是配菜。A君 再次把目光转移的 AspectJ 上,啥,你说 AspectJExpressionPointcutAdvisor?别闹,它不是,它只有切点表达式,还差通知呢。之前,A君 就把除 引介通知 外的其他通知,都归到了 AbstractAspectJAdvice 类下了。现在 Advice 的类图可是这样子的:

在这里插入图片描述

既然和 AspectJ 强绑定了,那就是给他个单独的实现吧。说是怎么说,实现起来也没啥东西。只是不把它归为通用实现罢了。A君 定义 AspectJPointcutAdvisor 类,代码如下:

import com.hqd.ch03.v20.aop.Pointcut;
import com.hqd.ch03.v20.aop.PointcutAdvisor;
import com.hqd.ch03.v20.aopalliance.aop.Advice;
import com.hqd.ch03.v20.core.Ordered;public class AspectJPointcutAdvisor implements PointcutAdvisor, Ordered {/*** 通知*/private final AbstractAspectJAdvice advice;/*** 切点*/private final Pointcut pointcut;private Integer order;public AspectJPointcutAdvisor(AbstractAspectJAdvice advice) {this.advice = advice;this.pointcut = advice.getPointcut();}@Overridepublic int getOrder() {if (this.order != null) {return this.order;} else {return this.advice.getOrder();}}public void setOrder(int order) {this.order = order;}@Overridepublic Advice getAdvice() {return this.advice;}@Overridepublic Pointcut getPointcut() {return this.pointcut;}public String getAspectName() {return this.advice.getAspectName();}
}

还有 引介通知,只是实现了不同接口,其他地方都是类似的。DefaultIntroductionAdvisor 代码如下:

import com.hqd.ch03.v20.aop.ClassFilter;
import com.hqd.ch03.v20.aop.DynamicIntroductionAdvice;
import com.hqd.ch03.v20.aop.IntroductionAdvisor;
import com.hqd.ch03.v20.aop.IntroductionInfo;
import com.hqd.ch03.v20.aopalliance.aop.Advice;
import com.hqd.ch03.v20.core.Ordered;import java.util.LinkedHashSet;
import java.util.Set;public class DefaultIntroductionAdvisor implements IntroductionAdvisor, ClassFilter, Ordered {private final Advice advice;private final Set<Class<?>> interfaces = new LinkedHashSet<>();private int order = Ordered.LOWEST_PRECEDENCE;public DefaultIntroductionAdvisor(Advice advice) {this(advice, (advice instanceof IntroductionInfo ? (IntroductionInfo) advice : null));}public DefaultIntroductionAdvisor(Advice advice, IntroductionInfo introductionInfo) {this.advice = advice;if (introductionInfo != null) {Class<?>[] introducedInterfaces = introductionInfo.getInterfaces();if (introducedInterfaces.length == 0) {throw new IllegalArgumentException("IntroductionInfo defines no interfaces to introduce: " + introductionInfo);}for (Class<?> ifc : introducedInterfaces) {addInterface(ifc);}}}public void addInterface(Class<?> ifc) {if (!ifc.isInterface()) {throw new IllegalArgumentException("Specified class [" + ifc.getName() + "] must be an interface");}this.interfaces.add(ifc);}@Overridepublic ClassFilter getClassFilter() {return this;}@Overridepublic void validateInterfaces() throws IllegalArgumentException {for (Class<?> ifc : this.interfaces) {if (this.advice instanceof DynamicIntroductionAdvice &&!((DynamicIntroductionAdvice) this.advice).implementsInterface(ifc)) {throw new IllegalArgumentException("DynamicIntroductionAdvice [" + this.advice + "] " +"does not implement interface [" + ifc.getName() + "] specified for introduction");}}}@Overridepublic Advice getAdvice() {return this.advice;}@Overridepublic Class<?>[] getInterfaces() {return this.interfaces.toArray(new Class[]{});}@Overridepublic boolean matches(Class<?> clazz) {return true;}@Overridepublic int getOrder() {return order;}public void setOrder(int order) {this.order = order;}
}
切面真正的管理者——Advised

    Advisor 只能表示单个切点和通知的关系,要是现在开发,通知往往不止一个,想要管理多个通知,需要再进行一次抽象。所谓管理者,那CRUD是基本功能。A君 之前因为没定义接口,不知道被 老大 骂了多少次,现在 A君 学聪明了,干啥都先定义个接口再说。A君 定义 Advised 作为管理 Advisor 的接口。代码如下:

import com.hqd.ch03.v20.aopalliance.aop.Advice;public interface ![Advised](https://i-blog.csdnimg.cn/direct/999c9f2cc48743e99c926ce85d3c5aa6.png)extends TargetClassAware {Advisor[] getAdvisors();default int getAdvisorCount() {return getAdvisors().length;}void addAdvisor(Advisor advisor);void addAdvisor(int pos, Advisor advisor);boolean removeAdvisor(Advisor advisor);void removeAdvisor(int index);int indexOf(Advisor advisor);boolean replaceAdvisor(Advisor a, Advisor b);void addAdvice(Advice advice);void addAdvice(int pos, Advice advice);boolean removeAdvice(Advice advice);int indexOf(Advice advice);}

这个接口没啥东西,就是基础CRUD。当然了,管理 Advisor 只是它的一部分功能,其他配置应该归它管理,这是后话了。照目前来说,Advised 极其简单。 AdvisedSupport 不在是之前的泥腿子了,它作为 Advised 标准实现,A君AdvisedSupport 类进行改造,让其实现 Advised 。改造如下:

import com.hqd.ch03.v20.aop.Advised;
import com.hqd.ch03.v20.aop.Advisor;
import com.hqd.ch03.v20.aop.DynamicIntroductionAdvice;
import com.hqd.ch03.v20.aop.IntroductionInfo;
import com.hqd.ch03.v20.aop.support.DefaultIntroductionAdvisor;
import com.hqd.ch03.v20.aop.support.DefaultPointcutAdvisor;
import com.hqd.ch03.v20.aopalliance.aop.Advice;
import org.apache.commons.collections4.CollectionUtils;import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;/*** advice管理类*/
public class AdvisedSupport implements Advised {AdvisorChainFactory advisorChainFactory = new DefaultAdvisorChainFactory();private List<Advisor> advisors = new ArrayList<>();private List<Class<?>> interfaces = new ArrayList<>();private Object target;public AdvisedSupport(Object target) {this.target = target;}public void addAdvisors(Collection<Advisor> advisors) {if (CollectionUtils.isNotEmpty(advisors)) {this.advisors.addAll(advisors);}}public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) {return this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this, method, targetClass);}public Advisor[] getAdvisors() {return advisors.toArray(new Advisor[]{});}@Overridepublic void addAdvisor(Advisor advisor) {if (advisor != null) {this.advisors.add(advisor);}}@Overridepublic void addAdvisor(int pos, Advisor advisor) {this.advisors.add(pos, advisor);}@Overridepublic boolean removeAdvisor(Advisor advisor) {return this.advisors.remove(advisor);}@Overridepublic void removeAdvisor(int index) {this.advisors.remove(index);}@Overridepublic int indexOf(Advisor advisor) {return this.advisors.indexOf(advisor);}@Overridepublic boolean replaceAdvisor(Advisor a, Advisor b) {int index = indexOf(a);if (index == -1) {return false;}removeAdvisor(index);addAdvisor(index, b);return true;}@Overridepublic void addAdvice(Advice advice) {addAdvice(this.advisors.size(), advice);}@Overridepublic void addAdvice(int pos, Advice advice) {/*** 根据通知类型,创建对于的Advisor*/if (advice instanceof IntroductionInfo) {addAdvisor(pos, new DefaultIntroductionAdvisor(advice, (IntroductionInfo) advice));} else if (advice instanceof DynamicIntroductionAdvice) {throw new RuntimeException("DynamicIntroductionAdvice may only be added as part of IntroductionAdvisor");} else {addAdvisor(pos, new DefaultPointcutAdvisor(advice));}}@Overridepublic boolean removeAdvice(Advice advice) {int index = indexOf(advice);if (index == -1) {return false;} else {removeAdvisor(index);return true;}}@Overridepublic int indexOf(Advice advice) {for (int i = 0; i < this.advisors.size(); i++) {Advisor advisor = this.advisors.get(i);if (advisor.getAdvice() == advice) {return i;}}return -1;}public List<Class<?>> getInterfaces() {return interfaces;}public void addInterfaces(Class<?>... classes) {this.interfaces.addAll(Arrays.asList(classes));}public Object getTarget() {return target;}@Overridepublic Class<?> getTargetClass() {return this.target.getClass();}
}

到这里,切面基本就完事了。A君 开始着手写测试代码,以 前置通知 为例。代码如下:

@Testpublic void v20() throws Throwable {System.out.println("############# 第二十版 Aspect(切面)篇 #############");com.hqd.ch03.v20.aop.framework.aspectj.AspectJExpressionPointcut pointcut =new com.hqd.ch03.v20.aop.framework.aspectj.AspectJExpressionPointcut("execution(* *.test*(..)) and args(msg)");Object ap = new AopBean();//前置通知AopTest aop = new AopTest();Method beforeTest = aop.getClass().getDeclaredMethod("beforeTestV19", JoinPoint.class, String.class);List list = new ArrayList<>();//前置通知com.hqd.ch03.v20.aop.framework.aspectj.AspectJMethodBeforeAdvice beforeAdvice =new com.hqd.ch03.v20.aop.framework.aspectj.AspectJMethodBeforeAdvice(pointcut, beforeTest, aop);Advisor advisor = new AspectJPointcutAdvisor(beforeAdvice);list.add(com.hqd.ch03.v20.aop.interceptor.ExposeInvocationInterceptor.ADVISOR);list.add(advisor);com.hqd.ch03.v20.aop.framework.ProxyFactory proxyFactory = new com.hqd.ch03.v20.aop.framework.ProxyFactory(ap);proxyFactory.addAdvisors(list);AopBean proxy = (AopBean) proxyFactory.getProxy();proxy.test1("11");}

测试结果如下:

在这里插入图片描述

整体来说,Aspect(切面) 还是比较简单的,并没有复杂的逻辑。如果没有历史包袱(详情见 第十九章 Spring之假如让你来写AOP——番外篇:Spring早期设计),相信会更为简单。不过为了广大使用者,兼容是必然的。“Aspect(切面) 也搞定了啦!” A君 欢呼。距离 AOP 实现又近了一步


总结

    正所谓树欲静而风不止,欲知后事如何,请看下回分解(✪ω✪)

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

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

相关文章

Matplotlib | 结合numpy中argsort函数来画出特征阶梯图

代码 #构建分类随机森林分类器 clfRandomForestClassifier(n_estimators10,random_state42,max_depth4) clf.fit(x_val, y_val) #对自变量和因变量进行拟合 for feature in zip(x_feature,clf.feature_importances_):print(feature)(V1, 0.0038989752714058486) (V2, 0.002703…

【GAT】 代码详解 (1) 运行方法【pytorch】可运行版本

GRAPH ATTENTION NETWORKS 代码详解 前言0.引言1. 环境配置2. 代码的运行2.1 报错处理2.2 运行结果展示 3.总结 前言 在前文中&#xff0c;我们已经深入探讨了图卷积神经网络和图注意力网络的理论基础。还没看的同学点这里补习下。接下来&#xff0c;将开启一个新的阶段&#…

Linux驱动开发(7):使用设备树实现RGB 灯驱动

通过上一小节的学习&#xff0c;我们已经能够编写简单的设备树节点&#xff0c;并且使用常用的of函数从设备树中获取我们想要的节点资源。 这一小节我们带领大家使用设备树编写一个简单的RGB灯驱动程序&#xff0c;加深对设备树的理解。 1. 实验说明 本节实验使用到 EBF6ULL-…

安卓手机root+magisk安装证书+抓取https请求

先讲一下有这篇文章的背景吧&#xff0c;在使用安卓手机fiddler抓包时&#xff0c;即使信任了证书&#xff0c;并且手机也安装了证书&#xff0c;但是还是无法捕获https请求的问题&#xff0c;最开始不知道原因&#xff0c;后来慢慢了解到现在有的app为了防止抓包&#xff0c;把…

【深度学习】循环神经网络及文本生成模型构建

循环神经网络 词嵌入层 ​ 词嵌入层的作用就是将文本转换为向量。 ​ 词嵌入层首先会根据输入的词的数量构建一个词向量矩阵&#xff0c;例如: 我们有 100 个词&#xff0c;每个词希望转换成 128 维度的向量&#xff0c;那么构建的矩阵形状即为: 100*128&#xff0c;输入的每…

51单片机基础01 单片机最小系统

目录 一、什么是51单片机 二、51单片机的引脚介绍 1、VCC GND 2、XTAL1 2 3、RST 4、EA 5、PSEN 6、ALE 7、RXD、TXD 8、INT0、INT1 9、T0、T1 10、MOSI、MISO、SCK 11、WR、RD 12、通用IO P0 13、通用IO P1 14、通用IO P2 三、51单片机的最小系统 1、供电与…

【前端知识】前端打包工具webpack深度解读

webpackandesign搭建前端脚手架 webpack概述一、核心功能二、主要特点三、核心概念四、使用场景五、安装与配置六、常用命令 配置文件详解一、基本结构二、主要配置项及其作用三、示例配置 加载器一、加载器的定义与作用二、常见的加载器类型及作用三、加载器的配置与使用四、加…

用vscode编写verilog时,如何有信号定义提示、信号定义跳转(go to definition)、模块跳转(跨文件跳转)这些功能

&#xff08;一&#xff09;方法一&#xff1a;安装插件SystemVerilog - Language Support 安装一个vscode插件即可&#xff0c;插件叫SystemVerilog - Language Support。虽然说另一个插件“Verilog-HDL/SystemVerilog/Bluespec SystemVerilog”也有信号提示及定义跳转功能&am…

uniapp vue3小程序报错Cannot read property ‘__route__‘ of undefined

在App.vue里有监听应用的生命周期 <script>// 只能在App.vue里监听应用的生命周期export default {onError: function(err) {console.log(AppOnError:, err); // 当 uni-app 报错时触发}} </script>在控制台打印里无意发现 Cannot read property ‘__route__‘ of …

Ubuntu20.04升级glibc升级及降级的心路历程

想使用pip安装Isaac Sim&#xff0c;无奈此方法只支持 GLIBC>2.34 。使用的是Ubuntu20.04&#xff0c;使用 ldd --version 查看GLIBC版本&#xff0c;如果版本低于 2.34 则需要升级GLIBC&#xff0c;基于此开始了长达一天的尝试。 请注意&#xff0c;升级GLIBC是一个危险操作…

奶龙IP联名异军突起:如何携手品牌营销共创双赢?

在快节奏的互联网消费时代&#xff0c;年轻消费群体对产品和品牌的要求越来越挑剔。因此在品牌年轻化的当下&#xff0c;一方面需要品牌自身形象也要不断追求时代感&#xff0c;另一方面品牌也需要不断引领消费者需求&#xff0c;提升竞争力和产品力。 奶龙作为近年来异军突起…

ROM修改进阶教程------安卓14去除修改系统应用后导致的卡logo验证步骤 适用安卓13 14 安卓15可借鉴参考

上期的博文解析了安卓14 安卓15去除系统应用签名验证的步骤解析。我们要明白。修改系统应用后有那些验证。其中签名验证 去卡logo验证 与可降级安装应用验证等等的区别。有些要相互结合使用。今天的博文将对修改系统应用后卡logo验证做个步骤解析。 通过博文了解💝💝�…

【Spring boot】微服务项目的搭建整合swagger的fastdfs和demo的编写

文章目录 1. 微服务项目搭建2. 整合 Swagger 信息3. 部署 fastdfsFastDFS安装环境安装开始图片测试FastDFS和nginx整合在Storage上安装nginxnginx安装不成功排查:4. springboot 整合 fastdfs 的demodemo编写1. 微服务项目搭建 版本总结: spring boot: 2.6.13springfox-boot…

Docker 篇-Docker 详细安装、了解和使用 Docker 核心功能(数据卷、自定义镜像 Dockerfile、网络)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 Docker 概述 1.1 Docker 主要组成部分 1.2 Docker 安装 2.0 Docker 常见命令 2.1 常见的命令介绍 2.2 常见的命令演示 3.0 数据卷 3.1 数据卷常见的命令 3.2 常见…

三格电子-EtherNet IP转Modbus TCP网关

EtherNet/IP转Modbus TCP网关 SG-EIP-TCP-210 产品用途 SG-EIP-TCP-210 网关可以实现将 Modbus TCP 接口设备连接到 EtherNet/IP 网络中。用户不需要了解具体的 Modbus TCP 和 EtherNet/IP 协议即可实现将 Modbus TCP 设备挂载到 EtherNet/IP 接口的 PLC 上&#xff0c;并和 …

修改gitee提交时用户名密码输错导致提交失败的解决方法

1、打开控制面板&#xff0c;点击用户账户 点击管理Windows凭据 点击Windows凭据&#xff0c;找到gitee,删除后重新git push 重新输入密码即可

Pytest-Bdd-Playwright 系列教程(10):配置功能文件路径 优化场景定义

Pytest-Bdd-Playwright 系列教程&#xff08;10&#xff09;&#xff1a;配置功能文件路径 & 优化场景定义 前言一、功能文件路径的配置1.1 全局设置功能文件路径1.2. 在场景中覆盖路径 二、避免重复输入功能文件名2.1 使用方法2.2 functools.partial 的背景 三、应用场景总…

安装gentoo之第二步:安装gentoo基本系统

目标 &#xff1a;将gentoo配置为服务器&#xff0c; 一、下载并解压stage文件 1.官网下载stage文件 网址&#xff1a;https://www.gentoo.org/downloads/ 将gentoo配置为服务器&#xff0c;所以只下载了stage3-amd64-systemd-20241117T163407Z.tar.xz这个最小的stage文件. 基…

Methode Electronics EDI 需求分析

Methode Electronics 是一家总部位于美国的全球性技术公司&#xff0c;专注于设计和制造用于多个行业的电子和电气组件&#xff0c;产品涵盖汽车、工业、电信、医疗设备以及消费电子等多个领域&#xff0c;提供创新的解决方案。 填写Methode_EDI_Parameters_Template Methode_…

Java基础知识(六)

文章目录 StringString、StringBuffer、StringBuilder 的区别&#xff1f;String 为什么是不可变的?字符串拼接用“” 还是 StringBuilder?String#equals() 和 Object#equals() 有何区别&#xff1f;字符串常量池的作用了解吗&#xff1f;String s1 new String("abc&qu…