【Spring(十一)】万字带你深入学习面向切面编程AOP
文章目录
- 前言
- AOP简介
- AOP入门案例
- AOP工作流程
- AOP切入点表达式
- AOP通知类型
- AOP通知获取数据
- 总结
前言
今天我们来学习
AOP
,在最初我们学习Spring时说过Spring的两大特征,一个是IOC
,一个是AOP
,我们现在要学习的就是这个AOP。
AOP简介
AOP:
面向切面编程
,一种编程范式,指导开发者如何组织程序结构。
作用:在不惊动原始设计的基础上为其进行功能增强
。
首先我们先来看看代码环境,在主方法中获取
BookDao对象
,并调用它的save()方法
,在BookDaoImpl
中save()方法是测试它的万次执行效率
,而此类中的别的方法没有这个功能👇👇。
BookDaoImpl
类
@Repository
public class BookDaoImpl implements BookDao {public void save() {//记录程序当前执行执行(开始时间)Long startTime = System.currentTimeMillis();//业务执行万次for (int i = 0;i<10000;i++) {System.out.println("book dao save ...");}//记录程序当前执行时间(结束时间)Long endTime = System.currentTimeMillis();//计算时间差Long totalTime = endTime-startTime;//输出信息System.out.println("执行万次消耗时间:" + totalTime + "ms");}public void update(){System.out.println("book dao update ...");}public void delete(){System.out.println("book dao delete ...");}public void select(){System.out.println("book dao select ...");}
}
主方法
public class App {public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);BookDao bookDao = ctx.getBean(BookDao.class);bookDao.save();}
}
运行结果
现在我们要说的不是这个,如上,
update()、delete()、select()方法
中并没有测试万次执行效率
这个功能,但是我在运行update()、delete()方法时,它也打印了执行万次消耗的时间,而select()方法没有👇👇。
这是因为我用了一种技术在
不惊动原始设计的基础上
想为谁追加功能就为谁追加功能,这就是AOP
,而不惊动原始设计也是我们Spring的一种理念:无入侵式/无侵入式
。
那它是怎么做的呢,我们来分析一下👇👇。
这是我们刚才的程序,在这里边观察红色框出来的四行,这是我们要执行的业务,除了这四行,我们蓝色框出来的是围绕着save()方法的业务上下两块东西,这两块东西实际上我也想让别的方法拥有,在这里为了让别的方法也有这个功能,我们就把这个东西抽取出来了👇👇。
将它抽取为一个单独的方法,叫什么不重要,注意看,我这里边拥有的内容和右边蓝色的内容完全对应,这是将我要每一个方法所具有的功能抽取出来得到的一个方法,这个时候就要区分一下了,在AOP中,我们称右边的save()、update()、select()、方法,也就是原始方法,叫它为
连接点
,而我们刚才是给每一个方法都追加功能了吗?没有,我们刚才只给update()、delete()方法追加了功能,select()没有追加,那么AOP对于要追加功能的这些方法,叫它切入点
,这个切入点就说明了哪些方法要追加功能,那按照刚才的现象来看,我们的select()属于不属于切入点,不属于,为什么,因为我刚才在追加功能的时候select()没有,接着说,我现在左边抽取出来的这个方法,这个方法叫什么呢,它说你要大家都有的功能,这是一组共性功能,它管这种共性功能叫通知
,接下来问题又来了,你说这个功能你这次做的要测它的性能,下次我们能不能做个别的功能,当然可以, 这种通知是不是可以开发好多个,可以根据我的需求开发第一种、第二种、第三种通知,那么问题就来了,你怎么知道在这俩方法上执行这个通知呢,我们是不知道的,因此看来在通知和切入点之间,还得有个东西,把它们俩绑定在一块,这样的话,一个通知就对应一个切入点,那么 这个东西叫什么呢,叫切面
,也就是说,切面描述的是你的这个通知的共性功能与对应的切入点的关系,有了这个关系它就知道了这俩方法对应这个通知,回头select()还有可能加入别的功能呢,到这里我们已经了解了这几个概念,最后我们再说一个, 因为通知是一个方法,在Java中我们不能直接写方法,我们把它放在一个类中,这个类给它个名称叫通知类
,到这里我们就得到了如下概念👇👇。
AOP入门案例
在前边我们已经介绍了AOP,现在我们来分析一下怎么做,并且去实现它,我们来实现的
入门案例
是:在接口执行前输出当前系统时间,在这里我们使用注解
的开发方式。
思路分析:
1.导入坐标(pom.xml)
2.制作连接点方法(原始操作,Dao接口与实现类)
3.制作共性功能(通知类,与通知)
4.定义切入点
5.绑定切入点与通知关系(切面)
分析完以后我们就要开始做了,首先来看一下代码环境👇👇。
-
BookDao接口
-
BookDao实现类
-
SpringConfig
-
主方法
-
执行save()方法
-
执行update()方法
我们可以通过执行结果看出 update()方法是没有获取当前系统时间这个功能的,现在我们要做的就是在不动原来代码的基础上,给update()方法增加获取当前系统时间的功能💪💪。
首先第一步,导入坐标,做AOP开发要导的坐标是spring-aop,但是这里需要说一点,看一下这个依赖关系👇👇。
在它的依赖关系中,
context
一旦导入,你会发现aop的包自动就导进来了,所以aop开发是默认导入的,除此之外还要导入一个aspectj
的包👇👇。
第二步就是制作连接点方法,也就是做好我们的实现类👇👇。
第三步就是把 获取当前系统时间的这个
共性功能
给它抽出来单独做,右键创建一个新的类:aop包下的MyAdvice类,在类中随便起一个方法,然后将功能添加进去👇👇。
public class MyAdvice {public void method(){System.out.println(System.currentTimeMillis());}
}
第四步我们要去定义它的
切入点
,先写一个私有的方法,方法名任意,但是不要冲突,方法里边有方法体,但是啥也没写,然后在它上边写一句话:@Pointcut()
,括号里边写什么呢,给它一个参数("execution()")
,execution自带一个括号,括号里边是描述我们刚才写的那个method方法的,怎么描述呢,它是这样一个方法,返回值是void类型
的,com.itheima包下的dao包下的BookDao接口里边的update()方法,没有参数:@Pointcut("execution(void com.itheima.dao.BookDao.update())")
,这句话就是告诉我们当执行到了这个方法的时候,这就是一个切入点。
//设置切入点,要求配置在方法上方@Pointcut("execution(void com.itheima.dao.BookDao.update())")private void pt(){}
第五步是绑定这个共性功能和这个切入点之间的关系,怎么绑,在这里我们假定需要让这个共性功能的方法在切入点前边执行,需要在共性功能的这个method方法上写上一个注解:
@Before("pt()")
。
//设置在切入点pt()的前面运行当前操作(前置通知)@Before("pt()")public void method(){System.out.println(System.currentTimeMillis());}
现在还是不能运行的,因为现在这段程序还得受
Spring控制
,但是它现在不受控,第一件事在类的上方加上@Component
让它变成Spring控制的bean
,写完以后虽然Spring能控制它,把它造成bean了,但是Spring并不知道你这里边是做AOP的,所以我们得告诉Spring,当它扫描到我以后我这个当AOP处理,所以在MyAdvice类上方再写一个注解:@Aspect
,这样就解决了这个问题了,加完这两个注解还不行,最后一个地方,配置类这,现在这里边不知道你整个程序里边是拿注解
开发的AOP
,所以我们还要在配置类中加一个注解来告诉它:@EnableAspectJAutoProxy
,现在我们整体看一下👇👇。
-
MyAdvice类
-
SpringConfig配置类
-
执行save()方法
-
执行update()方法
AOP工作流程
前边我们讲了AOP的入门案例,现在我们要来说说它的
工作流程
,对于AOP的整个工作流程,第一步应该做什么事呢,肯定是从Spring开始干活说起,所以第一步是Spring容器启动
,启动完以后是不是要进行初始化bean
的这些操作了,不着急,对于aop的工作流程来说,它要去读取所有切面配置中的切入点
,这里可没有说读取所有配置
的切入点。
为什么这么说呢,我们来看一下,这是我们刚才写的那段代码, 多了一句话,也就是在里边多配置了一个切入点,那这句话说的什么意思呢,也就是说如果你定义了一个切入点,并且在这使用了,那么这个切入点我读取,上边那个我不读取,也就是说它只读取你配置的,为什么这么说呢,我们后边要做的工作要与这个切入点有关,假定你配了100个切入点,但是你一个也没用,那不相当于没有配吗,所以在这个地方我们要看它配了的切入点。
把这配完以后,接下来就要
初始化bean,判定bean对应类中的方法是否匹配到任意切入点
,如果匹配失败
,创建对象
,然后获取bean,调用方法并执行,完成操作,我们主要说的是匹配成功
的情况,如果匹配成功,创建原始对象(目标对象
)的代理
对象,在这里边我们看到了一个熟悉的东西代理
, 前边我们学习过jdk的代理模式
,代理可以干什么事:增强
,也就是说我用代理对象
去调用对应的方法然后走我们增强的那些操作,那么aop内部是怎么做的呢,Spring的aop内部就是用代理模式
来实现的,看到这明白了,闹了半天它也是代理
。
接着说,除了这个概念以外,还有一个概念,叫
目标对象
,也就是你对那个对象做的代理,原始的那个对象我们叫目标对象
,如果匹配成功,创建原始对象(目标对象
)的代理
对象,那我们能不能想到执行操作的时候是一个什么样的流程,它在获取bean
的时候还是拿的哪个原始对象吗,当然不是,在它的Spring容器中保存的就是那个代理对象
,剩下的事就简单了,获取的bean是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作
,也就是说aop的整个工作实际上用了一套什么样的形式来做的,代理模式
,这就是aop的一个核心本质
,用的代理来做的。
接下来我们去代码中看👇👇。
现在我们运行不是为了看结果,在这执行不执行都不重要,我们要去打印一下这个
bookdao
对象,来看看这个对象是不是一个代理对象
👇👇。
现在我们在MyAdvice中修改配置的切入点,让它匹配不到,并再一次运行程序👇👇。
我们会发现,两次运行的结果几乎一模一样,难道出问题了吗,我们修改一下,在主方法中修改一下,再打印一下
这个对象的所属的字节码文件对象
👇👇。
也是一样,运行两次👇👇。
- 没配置切入点的
- 配置切入点的
通过观察字节码文件对象,我们会发现当配置了切入点后,会变成代理类型的对象,也就是说它最终用的是代理的对象,类型是这个没问题,但是你要打印对象时,它会按
BookDao
对象显示,为什么会这样,简单说一下,就是在aop的这套东西中间,它对它最终的那个对象的toString()方法
进行了重写,所以我们才会看到现在这种显示效果。
AOP切入点表达式
AOP的基础入门案例我们已经做完了,并且也知道内部它是用代理的形式进行工作的,下面我们就要针对AOP的各个环节的细节进行学习了,前边我们不是写了一个很长的式子,我们说那是它的切入点,现在我们就要研究那个东西了,那个东西不叫切入点,叫
切入点表达式
。
首先我们要知道这个式子写的时候到底有没有什么要求呢,也就是说要去学习它的
语法格式
,先说两个东西,切入点是我们要进行增强的方法,切入点表达式是要进行增强的方法的描述方式,注意一点:对于任意一个方法,它的描述方式是多种多样的,看一下我们现在做的方法👇👇。
对于这一个式子,它的大的描述方向就分
两种
,第一种方向是按接口
描述,第二种方向是按实现类
描述,现在这两种形式都可以👇👇。
接下来我们来说说这么复杂的一个式子,到底有什么规则没有,它是有严格的规则的👇👇。
这么写其实很容易,但是如果你的程序中有几百个几千个切入点,还要一一去写吗,当然不是,不然就累死了,所以我们接下来学习一种能简化这种一个一个去写的形式:
使用通配符描述切入点
。
在AOP中,可以使用通配符描述切入点,通配符干嘛的,加速配置,加速描述的,接下来我们看一下👇👇。
接下来再看一下它的
书写技巧
👇👇。
AOP通知类型
前边我们研究的是切入点表达式,也就是想控制给谁加aop就给谁加aop,现在我们再研究的细节点,
往哪加
,也就是AOP的通知类型
。
我们要介绍几种不同的通知类型,先来说第一个问题,我们现在aop实际上是从原来的功能中抽一部分出去做成共性功能,然后用的时候再加回来,问题来了,原来从前边挖走的你现在运行不能给它执行到后边吧,同样你原来从后边挖走的你现在运行不能给它执行到前边吧,所以说放的位置很讲究,一种有几种位置呢,一共有
五种位置
👇👇。
我们现在进入到程序中👇👇。
-
BookDao类
-
BookDao实现类
-
MyAdvice类
@Component
@Aspect
public class MyAdvice {@Pointcut("execution(void com.itheima.dao.BookDao.update())")private void pt(){}//@Before:前置通知,在原始方法运行之前执行public void before() {System.out.println("before advice ...");}//@After:后置通知,在原始方法运行之后执行public void after() {System.out.println("after advice ...");}//@Around:环绕通知,在原始方法运行的前后执行public Object around(ProceedingJoinPoint pjp) throws Throwable {System.out.println("around before advice ...");//表示对原始操作的调用Object ret = pjp.proceed();System.out.println("around after advice ...");return ret;}//@AfterReturning:返回后通知,在原始方法执行完毕后运行,且原始方法执行过程中未出现异常现象public void afterReturning() {System.out.println("afterReturning advice ...");}//@AfterThrowing:抛出异常后通知,在原始方法执行过程中出现异常后运行public void afterThrowing() {System.out.println("afterThrowing advice ...");}
}
- 主方法
我们提前在MyAdvice类中写了一些方法用来演示这五种通知类型,接下来我们去演示👇👇。
-
前置通知
-
后置通知
-
环绕通知
环绕通知是比较重要的,我们来分析一下,首先里边有两条打印语句,我们想要做的效果是在原始操作的前后,分别打印这两条语句,我们先按之前的运行一下👇👇。
运行完我们会发现两条打印语句出来了,但是我的原始操作怎么不见了,这是为什么呢,这是因为我们在这做的是
环绕通知
,环绕的话肯定是在原始操作的前和后,这里边有一个核心叫做那一句话代表的是对原始操作的调用呢?如果没有的话,你说这两个打印语句是一前一后,我还说这俩都是前呢,另一个人还说这俩都是后呢,所以这里边必须有一句话表示对原始操作的调用
,怎么做呢,格式非常固定,在你的around修饰
的通知上参数
写上一个叫ProceedingJoinPoint pjp
,定义完这个参数使用pjp.proceed()
,这一句话就代表对原始操作的调用👇👇。
但是他为什么会画红线呢,我们来解释一下,它告诉你要
抛异常
,为什么要抛异常呢,因为原始操作的调用你无法预料它有没有异常,所以在这需要先抛一个异常,让你强制的对这个东西进行处理👇👇。
现在我们来运行一下👇👇。
结果符合预期,这就叫做环绕通知,也是功能最强大的,说一个小细节,现在我的接口中除了有这个update()方法还有select()方法,我现在在主方法中调用这个select()方法👇👇。
因为现在程序中没有配置它的切入点,所以执行结果只有他自己的功能,现在我们去MyAdvice中定义一个全新的切入点 👇👇。
然后改一下刚才的环绕通知👇👇。
我们再来运行一下👇👇。
注意看,原始操作运行了嘛,运行了,环绕通知的前和后加上没有,加上了,但是最后
抛了一个异常
,这个异常是啥意思呢,解释一下,空的返回值从advice中出来了,它不匹配你的原始操作调用的返回值类型,原始操作的返回值是什么,是 int,那好解释一下这件事情,我们的环绕around对原始操作增强的时候,你记得原来如果没有返回值还好,但是如果原来有返回值的话,在环绕通知的最后边要将它的返回值扔出去,所以这个环绕通知的返回值也得改,改为什么呢,改为Object
,也就是返回一个对象,我们先返回一个200,运行一下,看程序有没有报错👇👇。
我们会发现首先
不会报错
,并且原始方法的返回值我已经篡改了,这里边的return将代表你原始操作的运行,简单一点就是原来你调你的select方法实际上走的是他对应的实现类,现在变成你先运行第一句打印,然后你运行原始操作,再运行第二句打印,再运行return,这个方法才算运行完,换句话说你原来的代理模式走完了,return的是这个200,那有人说那我的100哪里去了,在你运行原始操作那呢,这个方法有一个返回值,把这个返回值给它接出来,最后返回就行了💪💪。
-
返回后通知
-
抛出异常后通知
AOP通知获取数据
现在我们要来说说AOP里边的数据了,什么意思呢,就是现在我能够对你的功能进行干预了,加东西了,但是有一点,有些时候不是所有的情况都是统一处理的,比如说你过来一个参数,参数不一样我处理的方式不一样,这是我们经常见到的一些需求,那面对这种情况我们如果在通知里边拿不到我们原始通知的数据,你就玩不下去了,因此我们来说说
怎么样从AOP里边拿取通知的数据
。
那方式有多少种呢,有三种,第一种,获取参数,第二种,获取返回值,第三种,获取异常,对于这三个东西,我们需要分析一个问题,是不是所有的通知都有这些东西,当然不是,对于参数来说,所有的通知都能拿到,比如你现在调用一个方法,我不管你最后是正常结束还是异常结束了,你最起码调用的时候参数都有的呀,所以说参数是每个里边都有的,但是返回值就不是了,它必须得有保障原始操作正常执行,你才能在AOP中拿到原始操作的返回值,所以说,这里边只有两个东西能拿到返回值,哪两个呢,一个是返回后通知,一个是环绕通知,那接下来我们到程序中将这些信息拿一遍💪💪。
-
BookDao接口
-
BookDao实现类
-
主方法
接下来我们来
实现AOP获取数据
👇👇。
给方法里边设置一个
JoinPoint
参数,并通过getArgs()方法
获取参数,返回值是一个对象数组,通过Arrays.toString()去输出👇👇。
我们来改一改,给它设置两个参数,注意一点,我们的aop里边没动,注意看能拿到什么👇👇。
是不是都拿到了,后置通知也一模一样🎉🎉。
现在我们来说
环绕通知
,首先想一件事,JoinPoint是ProceedJoinPoint的父接口
,父接口都能调到的方法,子接口肯定能调到,在这就不打印它了,因为不是我们主体要说的,下面要说我们是不是在这调用原始方法了,注意调用原始方法的时候对于proceed这个操作,除了空参以外,还可以传递一个Object数组
👇👇。
也就是说我们可以把通过getArgs()获取的参数给proceed传进去用,写与不写代表的含义都一样,都代表使用getArgs()得到的参数来传递,我们来运行一下👇👇。
结果是没问题的,我们这里想说的是我要是给它使点坏,比如说在你获取参数以后,发送调用之前,我要把这里边的东西给改了,那会是为什么样呢?
我们会发现已经变成666了,现在我们已经做到了一个非常好的效果了,是如果传过来的参数有问题,我们就可以处理一下了💪💪。
接下来说
返回值
的获取,我们前边已经说过环绕通知了,现在只需要说一下返回后通知
🎉🎉。
在这里你如果想拿它的返回值,你可以先去定义一个用于接收返回值的形参,如下👇👇。
但是你要用这个东西,你必须告诉afterReturning你用ret这个变量准备接它的返回值,属性returning是专门干这事的👇👇。
这句话是什么意思,是如果你的原始方法有返回值,那就把返回值装到形参中叫ret的这个变量里,我们来运行一下👇👇。
在这里有一个问题,如果JoinPoint跟它同时存在,顺序必须是JoinPoint在前,如果不是第一个必报错👇👇。
接下来我们来说一下异常的,对于异常的怎么拿,在这虽然能拿到👇👇。
但是在这里边拿不到👇👇。
那怎么办呢,回归到最原始的 try catch中,这个 throwable就是你最终的异常对象了👇👇。
抛出异常后通知接收异常对象👇👇。
在这写完以后我们在原操作中抛异常👇👇。
注意看afterThrowing后边有一个异常信息,这个就是带过来的。
总结
以上就是我们学习AOP的全部内容,如果有什么错误的话,大家可以私信我📬📬,
希望大家多多关注+点赞+收藏 ^_ ^🙏🙏,你们的鼓励是我不断前进的动力💪💪!!!
相关文章:

【Spring(十一)】万字带你深入学习面向切面编程AOP
文章目录前言AOP简介AOP入门案例AOP工作流程AOP切入点表达式AOP通知类型AOP通知获取数据总结前言 今天我们来学习AOP,在最初我们学习Spring时说过Spring的两大特征,一个是IOC,一个是AOP,我们现在要学习的就是这个AOP。 AOP简介 AOP:面向切面编程,一种编程范式&#…...

基于Java+SpringBoot+Vue+uniapp前后端分离图书阅读系统设计与实现
博主介绍:✌全网粉丝3W,全栈开发工程师,从事多年软件开发,在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建、毕业项目实战、项目定制✌ 博主作品:《微服务实战》专栏是本人的实战经验总结,《S…...

2021年新公开工业控制系统严重漏洞汇总
声明 本文是学习ITOT一体化工业信息安全态势报告(2019). 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 工业互联网安全威胁 2021年新公开工业控制系统严重漏洞 缓冲区溢出漏洞 缓冲区溢出(buffer overflow&…...

Canvas鼠标滚轮缩放以及画布拖动(图文并茂版)
Canvas鼠标滚轮缩放以及画布拖动 本文会带大家认识Canvas中常用的坐标变换方法 translate 和 scale,并结合这两个方法,实现鼠标滚轮缩放以及画布拖动功能。 Canvas的坐标变换 Canvas 绘图的缩放以及画布拖动主要通过 CanvasRenderingContext2D 提供的 …...

[ECCV 2020] FGVC via progressive multi-granularity training of jigsaw patches
Contents IntroductionProgressive Multi-Granularity (PMG) training frameworkExperimentsReferencesIntroduction 不同于显式地寻找特征显著区域并抽取其特征,作者充分利用了 CNN 不同 stage 输出的特征图的语义粒度信息,并使用 Jigsaw Puzzle Generator 进行数据增强来帮…...

Python推导式
列表(list)推导式 [remove for source in xx_list]或者[remove for source in xx_list if condition] 实例: names[Bob,Mark,Mausk,Johndan,Wendy] new_names[name.upper() for name in names if len(name)<5] print(new_names)即迭代列…...

Java列表List的定查改增删操作
Java列表List的定查改增删操作定义查找遍历元素与下标互查修改增加删除java.util中提供了三种常用的集合类,列表List、集合Map和字典Set。这些集合类相较于数组有更多功能,并且都可以通过Iterator(迭代器)来访问。 在这篇博客中&…...

day03java语言特性 JDK、JRE、JVM
1、Java语言的特性 1.1、简单性在Java语言当中真正操作内存的是:JVM(Java虚拟机)所有的java程序都是运行在Java虚拟机当中的。而Java虚拟机执行过程中再去操作内存。对于C或者C来说程序员都是可以直接通过指针操作内存的。C或者C更灵活&…...

HydroD 实用教程(二)有限元模型
目 录一、前言二、模型种类三、单元类型四、FEM文件五、参考文献一、前言 SESAM (Super Element Structure Analysis Module)是由挪威船级社(DNV-GL)开发的一款有限元分析(FEA)系统,它以 GeniE、…...

Java中的Set集合
Set不能存储重复元素,元素无序(指的是不按照添加的顺序,List集合是按照添加顺序存储的)hashSet注:源码底层是hashMap实现的,因为hashMap是双列的,其中键是不能重复的,而hashSet是单列…...

【RabbitMQ五】——RabbitMQ路由模式(Routing)
RabbitMQ路由模式前言RabbitMQ模式的基本概念为什么要使用Rabbitmq 路由模式RabbitMQ路由模式组成元素路由模式完整代码Pom文件引入RabbtiMQ依赖RabbitMQ工具类生产者消费者1消费者2运行结果截图前言 通过本篇博客能够简单使用RabbitMQ的路由模式。 本篇博客主要是博主通过官网…...

【C语言】宏定义 结构体 枚举变量的用法
目录 一、数据类型 二、C语言宏定义 三、C语言typedef重命名 四、 #define与typedef的区别 五、结构体 六、枚举变量 补充学习一点STM32的必备基础知识 一、数据类型 二、C语言宏定义 关键字:#define 用途:用一个字符串代替一个数字,…...

锁升级之Synchronized
Synchronized JVM系统锁一个对象里如果有多个synchronized方法,同一时刻,只要有一个线程去调用其中的一个synchronized方法,其他线程只能等待!锁的是当前对象,对象被锁定后,其他线程都不能访问当前对象的其…...

基于nodejs+vue疫情网课管理系统
疫情网课也都将通过计算机进行整体智能化操作,对于疫情网课管理系统所牵扯的管理及数据保存都是非常多的,例如管理员:首页、个人中心、学生管理、教师管理、班级管理、课程分类管理、课程表管理、课程信息管理、作业信息管理、请假信息管理、上课签到管理、论坛交流…...

Zabbix 构建监控告警平台(三)
Zabbix User parametersZabbix Trigger1.Zabbix User parameters 1.1即自定义KEY 注意:mysql安装在被监测主机 [rootlocalhost ~]# yum -y install mariadb-server mariadb [rootlocalhost ~]# systemctl start mariadb [rootlocalhost ~]# mysqladmin -uroot statu…...

Linux系统之dool命令行工具的基本使用
Linux系统之dool命令行工具的基本使用一、dool命令行工具介绍二、本地系统环境检查1.检查系统版本2.检查系统内核版本三、下载dool软件包1.创建下载目录2.下载dool四、安装dool1.安装python32.安装dool五、dool的命令帮助六、dool的基本使用1.直接使用dool监控系统2.监控cpu和网…...

LeetCode-2335-装满杯子需要的最短总时长
1、堆 我们可以维护一个堆,首先我们将数组中不为0的数全部加入堆中,而后进行循环。当堆不为空时,我们将堆顶元素出堆并减一,而后观察是否还能继续出堆,若能则出堆,否则跳过,最后我们将处理后的…...

npm ERR! code ELIFECYCLE解决方案,npm犯错!myweb@1.0.0构建脚本失败。
1.问题npm ERR! code ELIFECYCLEnpm ERR! errno 1npm ERR! myweb1.0.0 build: webpack --config config/webpack.config.jsnpm ERR! Exit status 1npm ERR!npm ERR! Failed at the myweb1.0.0 build script.npm犯错!代码ELIFECYCLEnpm犯错!errno 1npm犯错!myweb1.0.0 build: we…...

最小二乘支持向量机”在学习偏微分方程 (PDE) 解方面的应用(Matlab代码实现)
目录 💥1 概述 📚2 运行结果 🎉3 参考文献 👨💻4 Matlab代码 💥1 概述 本代码说明了“最小二乘支持向量机”在学习偏微分方程 (PDE) 解方面的应用。提供了一个示例,…...

ISYSTEM调试实践8-winIDEA Analyzer功能1
前面几篇介绍了ISYSTEM的基本调试界面和功能,相比我之前用过的IDE,除了几种断点方式和脚本功能以外,应该都是比较简单,稍微操作一下就可以直接上手,后续我将介绍winIDEA的Analyzer 功能。 1 Analyzer简介 iSYSTEM An…...

每日学术速递2.11
CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.IR、cs.MM 1.A Comprehensive Survey on Multimodal Recommender Systems: Taxonomy, Evaluation, and Future Directions 标题:关于多模态推荐系统的综合调查:分…...

宝塔搭建实战php开源likeadmin通用管理admin端vue3源码(二)
大家好啊,我是测评君,欢迎来到web测评。 上一期给大家分享了server端的部署方式,今天来给大家分享admin端在本地搭建,与打包发布到宝塔的方法。感兴趣的朋友可以自行下载学习。 技术架构 vscode node16 vue3 elementPlus vit…...

网络基础-虚拟化工具-网桥
系列文章目录 本系列文章主要是回顾和学习工作中常用的网络基础命令,在此记录以便于回顾。 该篇文章主要是讲解虚拟化的工具网桥相关的概念和常用命令 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录系…...

剑指 Offer 14- II. 剪绳子 II
剑指 Offer 14- II. 剪绳子 II 给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m - 1] 。请问 k[0]k[1]…*k[m - 1] 可能的最大乘积是多少&a…...

English Learning - Day55 作业打卡 2023.2.9 周四
English Learning - Day55 作业打卡 2023.2.9 周四引言1. Jim 在看电视的时候他的老婆正在做饭。2. 他刚睡着电话就响了。3. 我正在想事情,这时忽然有人从后面抓我胳膊。4. 我们总是边吃火锅边唱歌。5. 他一听说出了事故,马上就来了现场。6. He entered …...

pixhawk2.4.8-地面站配置-APM固件
文章目录一、硬件准备二、软件准备1 已实飞测试2 MP地面站 任意版本下载:3 APM固件 任意版本下载:三、飞控校准1 刷固件2 机架选择3 加速度计校准4 指南针校准5 遥控器校准6 飞行模式7 紧急断电&无头模式8 基础参数设置9 电流计校准10 电调校准11 起…...

golang 通道类型
文章目录一、什么是通道类型二、通道产生的原因三、声明channel四、创建channel五、channel相关操作1、发送值2、接收值3、关闭通道3.1 注意3.2 特点四、通道类型1、无缓冲通道2、有缓冲通道五、单向通道一、什么是通道类型 Go 语言中的通道(channel)是一…...

并发、并行、吞吐量、延迟、响应时间 含义理解
并发、并行、吞吐量、延迟、响应时间 知识点了解 1. 响应时间(RT) 理解:响应时间是指系统对请求作出响应的时间。例如一个正在运行的服务,服务内程序接受到参数请求开始,到程序计算完,并将结果返回出去结束,这段时间…...

HTTP 和 HTTPS 的区别
文章目录前言一、HTTP 与 HTTPS 的基本概念HTTPHTTPS二、HTTP 和 HTTPS协议的区别前言 浏览网站时,我们会发现网址有两种格式,一种以http://开头,一种https://开头。好像这两种格式差别不大,只多了一个s,实际上他们有…...

微搭低代码从入门到精通07-基础布局组件
低码开发不同于传统开发,传统开发我们通常需要编写前端代码和后端代码。前端代码由HTML、CSS和JavaScript组成,后端代码我们通常要用后端语言比如Java来编写接口。 低码开发的特点是可视化开发,在编辑器中通过组件的拖拽来完成页面的编制。如…...