SpringBoot——IOC与AOP

news2024/9/21 17:18:49

文章目录

  • IOC AOP
  • 一、 分层解耦
    • 1.1 IOC - 控制反转 详细
    • 1.2 DI - 依赖注入 详解
  • 二、AOP
    • 2.1 了解
    • 2.2 快速入门 - AOP 开发步骤
      • 2.2.1 Maven依赖
      • 2.2.2 代码实现
      • 2.2.3 AOP 应用场景及优势
    • 2.3 核心概念
      • 2.3.1 连接点 - JoinPoint
      • 2.3.2 AOP执行流程
    • 2.4 通知
      • 2.4.1 通知类型
      • 2.4.2 通知顺序
    • 2.5 切入点表达式
      • 2.5.1 execution
        • 2.5.1.1 execution通配符
        • 2.5.1.2 execution表达式案例
        • 2.5.1.3 切入点表达式建议
      • 2.5.2 @annotation
      • 2.5.3 切入点表达式总结
    • 2.6 连接点

IOC AOP

一、 分层解耦

  • 内聚: 软件中各个功能模块内部的功能联系
  • 耦合: 衡量软件中各个层/模块之间的依赖、关联的程度
  • 软件设计原则:高内聚、低耦合

控制反转:Inversion Of Control,简称IOC。对象的创建控制权由程序自身转移到外部(容器),这种思想成为控制反转

依赖注入:Dependency Injection,简称DI。容器为应用程序提供运行时,所依赖的资源,称为依赖注入。

Bean对象: IOC容器中创建、管理的对象,称为bean

1.1 IOC - 控制反转 详细

把某个对象交给IOC容器管理,需要添加如下注解之一:

注解说明位置
@Component生命bean的基础注解不属于以下三类时,用此注解
@Controller@Component衍生注解标注在控制器
@Service@Component衍生注解标注在业务类上
@Repository@Component衍生注解标注在数据访问类上(由于与Mybatis整合,用的少)
  • 声名bean的时候,可以通过value属性指定bean的名字,如果没有指定,默认是类名首字母小写

  • 使用以上四个注解都可以生命bean,但是在Springboot集成web开发中,声名控制器bean只能用@Controller

  • bean的四大注解想要生效,需要被组件扫描注解@ComponentScan扫描

  • @ComponentScan注解虽然没有显示配置,但是实际上已经包含在了启动类生命注解@SpringBootApplication中,默认扫描的范围是启动类所在包及其子包

    如下包名是从“java”包后开始的,但是下面这种不推荐,我们希望的是按照Spring的规范,将包设置在启动类所在包及其子包

@ComponentScan({"dao","com.zhangjingqi"})

1.2 DI - 依赖注入 详解

@Autowired 注解默认是按照类型进行的,如果存在多个相同的bean,会报错。

EmpServiceA 实现 EmpService类,EmpServiceB 实现 EmpService类,我们在某个地方注入EmpService对象时便会出现注入错误。

解决方案

  • @Primary 设置bean的优先级

​ 如果我们想要哪个bean填入容器,可以在类名之上添加@Primary

  • @Qualifier 指定bean的名字
   @Qualifier("empServiceA")
   @Autowired
   private EmpService empService;
  • @Resource 按照名称注入

    @Autowired 注解默认按照类型注入,@Resource默认按照类名进行注入

   @Resource(name = "empServiceB")
   private EmpService empService;

二、AOP

2.1 了解

Spring的第二大核心,第一大核心是IOC

AOP:面向切面编程、面向方面编程,其实就是面向特定方法编程

实现

  • **动态代理是面向切面编程最主流的实现。**而SpringAOP是Spring框架的高级技术,目的是在管理bean对象的过程中,主要通过底层的动态代理机制,对特定的方法进行编程

  • 为什么要面向方法编程?

场景:案例部分功能运行较慢,定位执行耗时较长的业务方法,此时需要统计每一个业务方法的执行耗时,找到耗时较长的业务进行优化

​ 按照之前的方式,就是在方法开始前和开时候分别获取一个时间,两个时间相减就是执行耗时,但是这种方式是非常繁琐的

image-20230517160748642

如果我们基于AOP,面向方法编程,我们可以做到在不改动原始方法的基础上,来针对原始的方法进行编程,可以是对原始方法功能的增强,也可以改变原始方法的功能

比如我们现在要统计方法的耗时,我们只需要定义一个模板方法,将公共的代码定义在模板方法中

原始业务方法在这里指的是需要统计执行耗时的业务方法。而这样面向一个或者多个方法进行编程,就称为面向切面编程

比如我们调用list()方法,此时并不会直接执行原始的list方法,而是自动的去执行模板方法。

模板中所定义的代码逻辑其实是创建出来的代理对象方法中的逻辑

image-20230517161356224

2.2 快速入门 - AOP 开发步骤

需求:统计各个业务层方法执行耗时

2.2.1 Maven依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2.2.2 代码实现

针对于特定方法根据业务需要进行编程

@Slf4j
@Component //交给容器IOC进行管理
@Aspect //加上这个注解表示不是一个普通的类,而是一个AOP类,在此类中定义模板方法
public class TimeAspect {

//  参数是一个表达式,表示针对哪些特定方法进行编程
//  com.zhangjingqi.service 包名
//  第一个*代表任意返回值 第二个*代表类名或者接口名  第三个*代表方法名
    @Around("execution(* com.zhangjingqi.service.*.*(..))") //切入点表达式
    public Object recordTime(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        long begin = System.currentTimeMillis();

//      result 原始方法执行返回值
        Object result = proceedingJoinPoint.proceed();//调用原始方式运行

        long end = System.currentTimeMillis();

//      proceedingJoinPoint.getSignature() 获取方法的签名,我们就知道是哪个方法了
//      如: List com.zhangjingqi.service.impl.DeptServiceImpl.list()执行耗时:239ms
        log.info(proceedingJoinPoint.getSignature() + "执行耗时:{}ms", end - begin);

//      原始方法的返回值我们需要返回回去
        return result;
    }
}

2.2.3 AOP 应用场景及优势

应用场景

  • 记录操作日志
  • 权限控制
  • 事务管理

image-20230517164446034

优势

  • 代码无侵入
  • 减少重复代码
  • 提高开发效率
  • 维护方便

2.3 核心概念

2.3.1 连接点 - JoinPoint

连接点:JoinPoint,可以被AOP控制的方法(暗含方法执行时的相关信息)

通知:Advice,指那些重读的逻辑,也就是共性功能(最终体现为一个方法)

切入点:PointCut,匹配连接点的条件,通知仅会在切入点方法执行时被应用(就是实际被AOP控制的方法)

我们通常会使用下面的切入点表达式来描述切入点

@Around("execution(* com.zhangjingqi.service.*.*(..))") 

切面:Aspect,描述通知与切入点的对应关系(通知+切入点),被@Aspect注解修饰的类我们一般称为切面类

目标对象:Target,通知所应用的对象。

image-20230517175551925

2.3.2 AOP执行流程

通知如何与目标对象结合在一起对目标对象中的方法进行功能增强的?

​ ①SpringAOP是基于动态代理技术来实现的。程序运行的时候会自动的基于动态代理技术为目标对象生成一个对应的代理对象。

​ ② 在代理对象中就会对目标对象中的原始方法进行功能的增强。

如何来增强的?增强的逻辑是什么样子的?

​ 其实就是我们的通知

​ ③最终在Spring容器中注入的是代理对象,调用的方法也是代理对象中的对应方法

image-20230517180323565

2.4 通知

2.4.1 通知类型

  • @Around:环绕通知,此注解标注的通知方法在目标方法前、后都被执行,出现异常后后置代码不会执行。(因为原始方法出现异常了)
  • @Before:前置通知,此注解标注的通知方法在目标方法前被执行
  • @After:后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行
  • @AfterReturning:返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行
  • @AfterThrowing:异常后通知,此注解标注的通知方法在发生异常后执行
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Slf4j
@Component
@Aspect
public class MyAspect1 {
    //前置通知
    @Before("execution(* com.zhangjingqi.service.*.*(..))")
    public void before(JoinPoint joinPoint) {
        log.info("before ...");
    }

    //环绕通知
    @Around("execution(* com.zhangjingqi.service.*.*(..))")
    public Object around(ProceedingJoinPoint proceedingJoinPoint)
            throws Throwable {
        log.info("around before ...");
        //调用目标对象的原始方法执行
        Object result = proceedingJoinPoint.proceed();
        //原始方法如果执行时有异常,环绕通知中的后置代码不会在执行了
        log.info("around after ...");
        return result;
    }

    //后置通知
    @After("execution(* com.zhangjingqi.service.*.*(..))")
    public void after(JoinPoint joinPoint) {
        log.info("after ...");
    }

    //返回后通知(程序在正常执行的情况下,会执行的后置通知)
    @AfterReturning("execution(* com.zhangjingqi.service.*.*(..))")
    public void afterReturning(JoinPoint joinPoint) {
        log.info("afterReturning ...");
    }

    //异常通知(程序在出现异常的情况下,执行的后置通知)
    @AfterThrowing("execution(* com.zhangjingqi.service.*.*(..))")
    public void afterThrowing(JoinPoint joinPoint) {
        log.info("afterThrowing ...");
    }
}

注意事项

  • @Around环绕通知需要自己调用ProceedingJoinPoint.proceed()来执行原始方法,其他通知不需要考虑原始方法的执行

  • @Around环绕通知的方法的返回值,必须指定为Object,来接收原始方法的返回值

​ 如果不return,在调用这个方法的地方时拿不到返回值的

对切入点表达式进行抽取

//   生命切入点表达式的注解,切点
    @Pointcut("execution(* com.zhangjingqi.service.*.*(..))")
    private void pt(){

    }

    //前置通知
    @Before("pt()")
    public void before(JoinPoint joinPoint) {
        log.info("before ...");
    }

其他类中也可以进行抽取,只需要定位到切入点表达式的位置即可。

@Slf4j
@Component
@Aspect
public class MyAspect2 {
//引用MyAspect1切面类中的切入点表达式
@Before("com.zhangjingqi.aspect.MyAspect1.pt()")
public void before(){
log.info("MyAspect2 -> before ...");
 }
}

2.4.2 通知顺序

当有多个切面的切入点都匹配到了目标方法,目标方法运行,多个通知方法都会被执行。

下面研究多个切面类的通知顺序。同个切面类的通知顺序不再研究

  • 不同切面类中,默认按照切面类的类名字母排序

​ 目标方法前的通知方法:字母排名靠前的先执行

​ 目标方法后的通知方法:字母排名靠前的后执行

  • 使用@Order(数字)加在切面类上来控制顺序

2.5 切入点表达式

  • 切入点表达式:描述切入点方法的一种表达式
  • 作用:主要用来决定项目中哪些方法需要加入通知
  • 常见形式

​ execution(…):根据方法的签名来匹配

​ @annotation(…):根据注解匹配

2.5.1 execution

​ 主要根据方法的返回值、包名、类名、方法名、方法参数等信息来匹配

下面来描述的时候,可以基于接口。也可以基于实现类

execution(访问修饰符? 返回值 包名.类名.?方法名(方法参数) throws 异常?)

​ 其中?表示可省略的部分

  • 访问修饰符:可省略,比如public、protected
  • 包名.类名:可省略,但是不建议
  • throws 异常:可省略(注意是方法上声明抛出的异常,不是实际抛出的异常)

2.5.1.1 execution通配符

*:单个独立的任意符号,可以匹配任意返回值、包名、类名、方法名、方法参数等信息来匹配

​ 此案例表示返回值人任意,二级包任意,类或接口任意,方法参数任意但是有且只有一个

execution(* com.*.service.*.update(*)

匹配类名以Service结尾,方法以delete开头的方法

execution(void
com.itheima.service.impl.*Service.delete*(java.lang.Integer)
)

多个连续的任意符号,可以通配任意层级的包,或者任意类型、任意个数的参数

​ 层级包任意,方法的参数任意

execution(* com.zhangjingqi..DeptService.*(..)

返回值任意,方法名任意,方法参数任意

execution(* *(..)

2.5.1.2 execution表达式案例

@Pointcut("execution(* com.zhangjingqi.service.*.*(..))")
  • 省略异常
execution(public void
com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer)
)
  • 省略方法访问修饰符

    参数是全类名

execution(void
com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer)
)
  • 使用"…"省略包名
execution(public void  com..DeptServiceImpl.delete(java.lang.Integer))
  • 省略包名类名

​ 指定方法名。

​ 不建议将包名和方法名省略。一旦省略,将表达式的范围扩大,一是影响匹配的效率,而是可能匹配到其他不需要的方法

execution(public void  delete(java.lang.Integer))
  • 匹配所有的方法

​ 此时表示匹配DeptServiceImpl类中的所有方法

execution(public void  com..DeptServiceImpl.*(java.lang.Integer))
  • 使用 且(&&)、或(||)、非(!) 来组合比较复杂的切入点表达式

    execution(* com.zhangjingqi.service.DeptService.list(..)) ||
    execution(* com.zhangjingqi.service.DeptService.delete(..))
    

2.5.1.3 切入点表达式建议

  • 所有业务方法名在命名时尽量规范,方便切入点快速匹配。

​ 如查询方法find开头,更新类方法update开头

  • 描述切入点方法通常基于接口描述,而不是直接描述实现类,增强拓展性

  • 在满足业务需要的前提下,尽量缩小切入点的匹配范围

​ 包名匹配进行不使用“…”,使用“*”匹配单个包

2.5.2 @annotation

用于匹配标识有特定注解的方法

@Before("@annotation(com.zhangjingqi.anno.MyLog)")

简化下列表达式

execution(* com.zhangjingqi.service.DeptService.list(..)) ||
execution(* com.zhangjingqi.service.DeptService.delete(..))

实现步骤

  1. 编写自定义注解

  2. 在业务类要做为连接点的方法上添加自定义注解

创建自定义注解类

@Retention(RetentionPolicy.RUNTIME)//描述注解什么时候生效的:运行时有效
@Target(ElementType.METHOD)//当前注解可以作用在哪些地方
public @interface MyLog {
}

添加自定义注解

@Override
@MyLog //自定义注解(表示:当前方法属于目标方法)
public List<Dept> list() { ... }

@Override
@MyLog //自定义注解(表示:当前方法属于目标方法)
public void delete(Integer id) { ... }

切面类

@Slf4j
@Component
@Aspect
public class MyAspect6 {
//针对list方法、delete方法进行前置通知和后置通知
//前置通知
@Before("@annotation(com.zhangjingqi.anno.MyLog)")
   public void before(){
   log.info("MyAspect6 -> before ...");

//后置通知
@After("@annotation(com.zhangjingqi.anno.MyLog)")
   public void after(){
   log.info("MyAspect6 -> after ...");
 }
}

2.5.3 切入点表达式总结

  • execution切入点表达式

​ 根据我们所指定的方法的描述信息来匹配切入点方法,这种方式也是最为常用的一种方式

​ 如果我们要匹配的切入点方法的方法名不规则,或者有一些比较特殊的需求,通过

​ execution切入点表达式描述比较繁琐

  • annotation 切入点表达式

​ 基于注解的方式来匹配切入点方法。这种方式虽然多一步操作,我们需要自定义一个注解,但

​ 是相对来比较灵活。我们需要匹配哪个方法,就在方法上加上对应的注解就可以了

2.6 连接点

被AOP控制的方法,目标对象中所有的方法都可以被AOP控制,在Spring AOP中又特制方法的执行

image-20230517175551925

  • 在Spring中用JoinPoint抽象了连接点,用它可以获得方法执行时的相关信息,如目标类名、方法类名、方法参数等

​ 对于@Around通知,获取连接点信息只能用ProceedingJoinPoint

image-20230520103824339

​ 对于其他四种通知,获取连接点信息只能使用JoinPoint,它是ProceedingJoinPoint父类型

image-20230520103842552

对于@Around通知,为什么获取连接点信息只能用ProceedingJoinPoint?

 在Spring AOP中,@Around通知是最为强大和灵活的通知类型,它可以决定是否执行连接点,以及如何处理连接点返回的结果。因此,@Around通知需要通过ProceedingJoinPoint参数来获取连接点信息。

​ ProceedingJoinPoint是JoinPoint的子类,同时也是JoinPoint的扩展版本。JoinPoint表示连接点,也就是被Advice修饰的方法。而ProceedingJoinPoint除了表示连接点外,还具有一个proceed()方法,该方法是执行目标方法的关键。在@Before和@After通知中,JoinPoint足以满足需要,因为它们只需要获取连接点信息即可,不需要执行目标方法。但在@Around中,除了获取连接点信息,还需要控制目标方法的执行,因此需要用到ProceedingJoinPoint。

​ 在@Around通知中,可以通过ProceedingJoinPoint的proceed()方法,手动控制目标方法的执行。例如,可以在proceed()方法前后进行一些预处理或后处理。同时,ProceedingJoinPoint还提供了一些其他的工具方法,例如getArgs()获取目标方法参数,getSignature()获取目标方法签名等,这些方法在编写@Around通知时也非常有用。

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

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

相关文章

百度贴吧视频发布软件视频教程(操作十分简单)

百度贴吧视频发布软件视频教程(操作十分简单) 软件有月卡、季卡、半年卡、年卡 【有时软件个别卡种售空&#xff0c;价格有上涨下降&#xff0c;关注获取当日价格】 我现在正在发帖的一个实时的一个画面&#xff0c;就是有很多同学问我就是喜羊羊今天还好跑吗&#xff0c;明天…

linux入门---通信的理解和匿名管道

这里写目录标题 为什么有通信通信的两个标准通信的本质管道通信的本质如何实现管道通信管道文件的特点管道的特征如何理解指令上的管道 为什么有通信 在我们的生活中有很多地方都需要用到通信&#xff0c;比如说出去玩要告诉伙伴们我们到哪了&#xff0c;做一件事的时候得通过…

MySQL基础(三十七)主从复制

1. 主从复制概述 1.1 如何提升数据库并发能力 此外&#xff0c;一般应用对数据库而言都是“ 读多写少 ”&#xff0c;也就说对数据库读取数据的压力比较大&#xff0c;有一个思路就是采用数据库集群的方案&#xff0c;做 主从架构 、进行 读写分离 &#xff0c;这样同样可以提…

第四十六天学习记录:C语言进阶:KMP算法个人学习方法

学习了strstr库函数后&#xff0c;老师让了解KMP算法&#xff0c;这也算是我接触到的第一个算法。 由于这一块得自己翻资料自学&#xff0c;因此初识比较吃力。 后面根据自己的理解方式&#xff0c;个人认为理解KMP算法最关键点就是理解next数组是怎么生成的。 下面说说我理解n…

对于西瓜书神经网络的c#手写版本

本文根据西瓜书第五章中给出的公式编写&#xff0c;书中给出了全连接神经网络的实现逻辑&#xff0c;本文在此基础上编写了Mnist10手写10个数字的案例&#xff0c;网上也有一些其他手写的例子参考。demo使用unity进行编写&#xff0c;方便且易于查错。 该案例仅作为学习&#x…

CSS零基础快速入门(详细教程)

1&#xff0c;CSS概述 CSS是层叠样式表&#xff0c;由Cascading Style Sheets简称而来。 CSS的功能为&#xff1a;能够对网页中元素位置的排版进行像素级精确控制&#xff0c;实现美化页面的效果&#xff0c;并且能够做到页面的样式和结构分离。 CSS的作用效果跟我们日常使用…

这个 快速排序详解过程 我能吹一辈子!

文章目录 快速排序概念快速排序递归实现Hoare版本挖坑法前后指针法 快速排序非递归实现Hoare版本挖坑法前后指针法 快速排序的俩个优化三数取中小区间优化 快速排序概念 快速排序是公认的排序之王&#xff0c;快速排序是Hoare于1962年提出的一种二叉树结构的交换排序算法&#…

【Linux】静态库与动态库

前言 对于C/C的学习者&#xff0c;我们经常听到C/C的标准库&#xff0c;我们也经常使用它们&#xff0c;但是我们在使用的时候经常只包含一下头文件&#xff0c;然后就使用了&#xff0c;我们从来没有认真的研究过C/C的标准库&#xff0c;而且C/C的头文件中只有声明并没有声明的…

文件上传,解析漏洞编译器安全(23)文件上传为什么加空格和修改为其他符号(例如换行符)问题

apache低版本解析漏洞 这个网站目录里有两个文件&#xff0c;一个是正常的php文件&#xff0c;另一个xx.php.xxx&#xff0c;源码是php源码&#xff0c;命名的文件&#xff0c;而访问中xxx的文件依旧可以执行出php代码的结果&#xff0c;而xxx就能当php文件解析&#xff0c;这…

uvc摄像头驱动uvc设备的注册分析

uvc摄像头驱动uvc设备的注册分析 文章目录 uvc摄像头驱动uvc设备的注册分析uvc_inituvc_probeuvc_register_videouvc_register_chainsuvc_register_termsuvc_register_video uvc_ioctl_opsuvc_fops uvc_init /driver/media/usb/uvc/uvc_driver.c /** UVC 驱动结构体*/ struct…

每日学术速递5.19

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.On the Hidden Mystery of OCR in Large Multimodal Models 标题&#xff1a;论大型多模态模型中 OCR 的隐藏奥秘 作者&#xff1a;Yuliang Liu, Zhang Li, Hongliang Li, Wenwen…

计算机图形学-GAMES101-11

显式几何的表示方法 Point Cloud 使用一系列点表示物体的表面。理论上可以表达空间中任何物体。如物体扫描会得到空间中一系列点&#xff0c;但在建模时我们要提取出大量多边形的面。点云对密度要求很高&#xff0c;因此不常使用。 Polygon Mesh 使用三角形表示物体。涉及三…

备份树莓派SD卡 — 保姆级教学

在我们树莓派项目开发的过程中&#xff0c;经常遇到以下问题&#xff1a; 1.自己辛辛苦苦开发出来的项目&#xff0c;害怕内存卡损坏&#xff0c;系统被破坏掉&#xff0c;想做一个备份。 2.自己的树莓派内存卡内存不够了&#xff0c;想将原来卡上的内容放在新的大容量内存卡…

Arm微架构分析系列3——Arm的X计划

1. 引言 前文介绍了Arm公司近几年在移动处理器市场推出的Cortex-A系列处理器。Cortex-A系列处理器每年迭代&#xff0c;性能和能效不断提升&#xff0c;是一款非常成功的产品。但是&#xff0c;Arm并不满足于Cortex-A系列每年的架构小幅度升级&#xff0c;又推出了X计划&#x…

esp32CAM环境安装教程---串口驱动安装

前言 &#xff08;1&#xff09;本人安装好arduino 的ESP32环境之后&#xff0c; 发现一直下载不进去程序。一直说Cannot configure port, something went wrong. Original message: PermissionError。 &#xff08;2&#xff09;查阅了很多资料&#xff0c;用了各种办法&#…

怎么消除文法的左递归性

除文法的左递归性可以采用以下方法&#xff1a; 直接左递归转换为间接左递归消除间接左递归 举例说明&#xff1a; 直接左递归转换为间接左递归 原文法&#xff1a;A → Aα | β 转换后的文法&#xff1a;A → βA A → αA | ε 例如&#xff1a;S → Sabc | ε 转换后…

4. QT中的事件函数 --- 鼠标事件、键盘事件、定时器事件、绘图事件

1. 说明 在QT的控件或者窗口当中&#xff0c;如果对于当前鼠标或者键盘的功能需要自己定义&#xff0c;可以重写父类当中对应虚函数&#xff0c;主要包括以下几个&#xff1a; //键盘按键按下 virtual void keyPressEvent(QKeyEvent *event); //键盘按键抬起 virtual void ke…

11.1网络编程

多线程 一、基础知识概念相关API二、任务创建一个简单的本地客户端迭代服务器select系统调用并发服务器数据报三、总结四、问题一、基础知识 概念 网络编程中客户端和服务器指的是进程,而不是常提到的机器或者主机。注意三个概念:请求、响应、事务。 网络编程中客户端-服务器…

面向对象的三大特性之继承(C++)

文章目录 继承的概念和定义概念定义定义格式继承关系和访问限定符继承基类成员访问方式的变化 基类和派生类对象赋值转换继承中的作用域派生类的默认成员函数继承与友元继承与静态成员菱形继承与虚拟继承菱形继承虚拟继承 继承的总结与反思继承和组合 继承的概念和定义 概念 继…

微信小程序 nodejs+vue+uniapp付费自习室图书馆教室座位系统-

系统分为用户和管理员角色 管理员的主要功能有&#xff1a; 1.管理员输入账户登陆后台 2.个人中心&#xff1a;管理员修改密码和账户信息 3.用户管理&#xff1a;对注册的用户信息进行添加&#xff0c;删除&#xff0c;修改&#xff0c;查询 4.自习室管理&#xff1a;对系统的自…