说在前面:为了准备Java后端开发准备的面经,有的摘自其他网站,有平时面试总结的。分享一下。
Java开发基础
0. 抽象与界面的区别
语法:
结构器-抽象类可以,接口不能
具体方法-抽象类可以有,界面都是抽象方法,但1.8后可以有默认方法
成员修饰符-界面全是public,抽象类可以是private、默认、protected、public
继承和实现——抽象只能是单一继承,但多个接口可以实现
成员变量可以在抽象类中定义,接口中的成员变量实际上是常量
静态方法-抽象类中可以有,接口中不能有
抽象类必须声明为抽象类,但抽象类不一定有抽象方法
语义:
抽象是指概念
接口描述的是一种特征
1. 反射机制
通过getclass方法动态获取类的实例,可以提高灵活性和可扩展性,但会消耗一定的系统资源,也会忽略权限检查,
实例可以获得类的私有属性和方法
2. 序列化和反序列化
序列化是将类对象状态转换为二进制文件保存到磁盘中;
反序列化是根据编号组将保存在硬盘中的Java对象状态的字节序列转换为类对象,恢复到内存中。
3. String、Stringbuffer和Stringbuilder的区别?
String 固定尺寸,不可变
StringBuffer 大小可变,线程安全(锁定),同步,效率低,适用于多线程,并发性低
StringBuilder 大小可变,线程不安全(无锁),不同步,效率高,适用于单线程,并发性高
4. 总结List问题
ArrayList 底层数组在搜索元素时效率高(每个元素都有相应的索引),线程不安全,不同步
LinkedList 基于双向链表,插入和删除元素时效率高(链表不需要内存移位)线程不安全,不同步
Vector 线程安全,同步,操作类似于ArrayList
5. hashtable和hashmap的区别
hashtable线程安全,但性能不高,一般使用concurenthashmap。hashtable同步,hashmap不同步
前者的键值不允许为空 后者允许
前者基于hashcode,后者基于hash值
前者使用Enumeration遍历,后者使用iteration遍历
6.泛型的作用:泛型实际上是数据类型的参数化
消除强制性类型转换;提高代码重用性;提高Java程序类型的安全性
7.创建线程的方法
继承Thread类,重写run方法
接口Runnnable或Callable接口
线程是通过线程池创建的
8.Runnnable和Callable的区别
前者不会返回结果并抛出异常 后者会
9.slep和wait的区别
sleep法是thread类的静态方法,wait法是object类的方法
sleep不会释放锁 wait将被释放
wait必须用于同步方法和同步代码块 slep方法可以在任何地方使用
sleep必须捕获异常 wait不需要捕获异常
10.synchronized 底层实现原理?
synchronized修改后的代码块和方法可以防止其他线程获得当前对象的监控锁,并确保
只有一种方法可以同时进入临界区,
它还将确保所有操作都直接刷入内存,以确保内存的可见性。
11.volatile底层实现原理?
只能修改变量,不能修改方法和代码块
volatile修改后的变量将直接同步更新到内存中,以确保其他线程立即看到更新后的变量值
不能保证原子性(原子性要么做,要么不执行)
还能保证有序性
12.线性安全
Vector:只要是关键操作,方法前面加了synchronized关键词,保证线程的安全
Hashtable:使用synchronized关键字,因此与Hashmap相比,线程安全。
ConcurrentHashMap:采用锁分段技术保证线性安全,是一种高效但线程安全的集合。
Stack:栈,也是线程安全的,继承于Vector。
13.线性不安全
HashMap HashSet TreeSet TreeMap ArrayList LinkedList
14.Synchronized和Lock的区别
前者是关键字 后者是接口
前者会自动释放锁 后者不会 必须在finally中释放锁
前者是非中断锁,必须等到线程执行后才能释放锁 后者是中断锁
前者在竞争不太激烈时表现良好 后者更灵活,适用于竞争场景
15.GET提交和POST提交的区别
前者直接在提交地址后面添加表单数据,大小有限,但速度快
后者提交表单数据作为一个整体数据块,没有大小限制,但速度很慢
16. 类加载过程
事实上,Java字节码文件被加载到机器内存中,包括编译和执行两个过程。编译是通过Javac生成Java文件.class文件,运行就是.将class文件交给虚拟机执行。
类加载过程是指虚拟机将.将class文件中的类信息加载到内存中,并对相应的class对象进行分析和生成。
17.双亲委派机制
根据层次关系逐层加载并分配。如果您想加载一个类别,从底层到高层,应用层加载器首先加载,然后向上分配到扩展加载器,最后向上分配
启动式加载器,如果失败,将失败信息从上层加载到下层,最终找不到,报告类找不到异常。
优点是保证安全,层次也代表优先级,启动式加载器优先级最高,也可以避免重复加载,父加载器加载,子加载器不需要再加载
18. JVM内存模型
(1)方法区:属于JVM运行时数据区域的逻辑区域,是各线程共享的内存区域;
类信息、字段信息、方法信息、常量、静态变量、即时编译器编译的代码缓存等数据将加载虚拟机从字节码文件中读取和分析;
方法区与元空间的关系类似于接口与类的关系。
(2)堆:JVM管理的最大内存空间是所有线程共享、存储对象实例和数组;
它是GC(垃圾收集器)管理的主要区域。GC主要采用分代垃圾收集算法,分为年轻一代、老一代和元空间(取代1.7中的永久一代)。
(3)虚拟机栈:随线程创建创建周期,随线程结束而死亡,是线程私有的;
栈帧作为虚拟机栈的基本单位,由局部变量表、操作数栈、动态链接、方法返回地址组成。
动态链接主要服务于一种调用其他方法的方法场景,因为在字节码文件中,所有变量和方法的引用都作为符号引用和保存
在Class文件的常量池中,动态链接的作用是将符号引用转换为调用方法的直接引用;
Stackoverflowerror(死循环)和outofmemoryeror(内存扩展时未申请空间)将出现两种异常。
(4)本地方法堆栈:与虚拟堆栈的周期和堆栈帧的组成结构非常相似。本地方法堆栈是指在调用本地方法(可能是Java语言以外的其他语言的方法界面)时创建的堆栈空间。
(5)程序计数器:字节码解释器通过改变程序计数器依次读取指令,实现循环、选择、顺序执行和异常处理;
同时,在多线程的情况下,每个线程都有一个独立的程序计数器,可以在线程切换后正常运行;
没有内存溢出,随线程创建而创建,随线程结束而死亡。
19.GC回收JVM
(1)内存分配与回收原则:
大多数对象分布在新一代Eden区,如果空间不足,GC将启动一次;
大对象直接进入老年,如需要连续存储空间的数组和字符串;
长期存活的对象将进入老年,给每个对象一个年龄计数器。如果GC的回收仍然存在,年龄值将增加1。如果超过15,它将进入老年;
(2)判断死亡对象的方法:
引用计数法:不常用,使用引用计数器,每引用一次加1,引用失效时减1,直至0,判断为死亡对象;
可达性分析算法:当对象到达GC时 在没有引用链的情况下,Roots被认为需要回收。
(3)垃圾收集算法:
标记清除算法:两个阶段-标记和清晰阶段,首先标记所有不需要回收的对象,并在标记完成后统一回收所有未标记的对象。缺点是效率低,会产生不连续的碎片空间。
标记-复制算法:首先将内存分为两个相同大小的块。每次使用一个块后,将存活的对象集中在另一个块中,然后清理所有使用的空间,以便每次只回收一半的内存。
标记-整理算法:与标记-清除的标记阶段相同,所有生存对象在标记后移动到连续端,然后清除剩余端边界以外的内存。
分代收集算法:将虚拟机的堆内存分为新一代和老一代。新一代通常每次收集都会有大量的物体死亡,所以您可以选择标记-复制算法,每次垃圾收集只需支付少量的物体复制成本;
对象在老年人中存活的概率较高,因此一般选择标记-清除或标记-整理算法进行垃圾回收。
20.jdk8的新特性
(1)Lambda表达式允许函数作为方法参数或代码作为数据,使代码更加简洁;
(2)Optional类,能很好地解决空指针异常。
(3)Base64是一种编码算法,可以将非ASCII码字符转换为ASCII码字符,而不是加密算法。
(4)界面可以有默认方法和静态方法,默认方法可以大大提高程序的可扩展性,但也容易造成歧义。
(5)引用方法,可直接引用现有Java类或对象(实例)的方法或构造器。
基本引用格式如下:Class::method(静态或普通方法)或Classs::new或实例对象::方法
(6)Stream流,可以通过声明处理数据,可读性强,可以轻松获得并行流,不需要自己写
多线程代码,更注重业务逻辑。
(7)注释可以在同一个地方重复在局部变量、泛型类中,多次添加注解,扩展注解,
注释可以添加到父类和界面的实现和异常。
(8)并行数组可以提高数组排序的速度。
(9)并发,在ConcurentHashMap方法中增加了支持聚集操作的新方法。
(10)新日期API解决了以前的许多问题。
21. countdownlatch和cyclicbarier的区别以及各自的应用场景?
22.hashmapput()和get()实现原理
23.object有哪些方法?
clone() equals() toString()
finalize()-当垃圾回收器确定对象不被引用时,对象的垃圾回收器调用此方法
getClass()获取此时运行时的类别
hashCode()返回对象的哈希值
notify()唤醒对象监视器上等待的单个线程 notifyAll()唤醒所有线程
wait() 让当前线程进入等待
24.String内存问题
String字符串变量一般直接定义在字符串常量池中,而字符串常量池在jvm内存的堆叠空间中。如果new对象创建的字符串变量,肯定会在内存堆中创建一个字符串常量池(前提是字符串常量池中没有这个值),所以只要是直接定义的字符串变量,就会出现同一个地址,new就不一样了。
数据库
1.Sql优化
使用适当的索引进行查询
避免空判断句
避免左侧模糊查询
避免使用in 用exists代替
避免使用or 和 不等于条件查询
避免在 where 表达式操作和函数操作的字段在句子中
尽可能多地使用 varchar 代替 char
避免使用“*”返回一切,可以用特定的字段代替
以上会导致数据库引擎放弃索引进行全表扫描
2.索引及其类型和类型是什么?
索引是一种数据结构,专门用于帮助用户在数据库中快速查询数据
(就像新华字典的目录一样)
类型:BTREE、RTREE、HASH、FULLTEXT
类型:普通索引、唯一索引、主键索引、全文索引、组合索引
什么情况适合使用索引?
数据量大的表,
经常查询的字段,
字段的主键和外键,
通常与其他表连接的表中连接字段,
经常做排序字段,
常用于where子句,order by、group by的字段
3. 什么情况下不适合索引?
查询很少使用的情况不适合建立索引;
经常添加、删除和更改的字段不适合建立索引;
当数据太少时,不适合建立索引;
定义为text, image和bit数据类型列不适合建立索引
4. 聚集索引和非聚集索引
收集索引的索引和数据放在一起 非聚集索引是分开的,而不是聚集索引
物理上连续记录前者存储,逻辑上连续记录后者
前者每张表只能有一个 一张表可以有多个后者
前者物理存储按索引排序,后者不按索引排序
5.事务和属性是什么?
事务可以包括一个或多个操作,要么成功提交,要么失败后回滚
事务属性:
原子性
一致性: 执行事务前后,数据保持一致,多个事务读取同一数据的结果相同;
隔离:指各操作之间不相互干扰 隔离等级:未提交阅读 读已提交 可重复读 串行化
持久性:事务提交后,数据库的变化是持久的
6.事务并发问题?
脏读:事务A读取事务B修改的数据,然后B事务回滚,所以A读取的数据是脏数据
不要重复阅读:当事务A读取数据时,事务B不断修改,每次结果都不同,即重复阅读
幻读:事务A将数据从分数改为等级。在这个过程中,事务B插入了一个新数据,而这个数据没有改变就是幻读。
悲观锁:在操作数据时加锁,直到操作结束
乐观锁:操作前不加锁。只有在提交时,才能根据版本号进行比较,验证数据是否存在冲突
7. 事务的隔离级别
阅读未提交:事务A正在编写数据,但事务B必须申请阅读。此时,事务A尚未提交修改后的数据,因此事务B将出现
读取A未提交的数据;
读取已提交:事务A在读取,事务B在写,等待事务A再读取只能读取提交后的数据。
可重复读取:在一个事务中,其他事务在结束前不能读写数据,以便在同一事务中读取两次
数据是一样的。
可序列化:最高隔离级别,同时只能执行一个事务,即串行,不能并发执行。
8.存储引擎
(1)MySQL系统结构
连接层
服务层
引擎层
存储层
(2)存储引擎
InnoDB发动机:默认MySQL存储发动机,高可靠性和高性能的通用存储发动机。
特点:DML操作遵循ACID模型,支持事务;
提高并发访问性能的行级锁;
支持外键约束,确保数据的完整性和正确性;
文件:每个表对应一个表空间文件,存储结构、数据和索引
MyISAM引擎:MySQL早期默认存储引擎
特点:不支持事务,不支持外键
支持表锁,行级锁,
访问速度快
文件:xxx.sdi 存储表结构信息 xxx.MYD 存储数据 xxx.MYI 存储索引
Memory引擎:如果表数据存储在内存中,则会受到硬件和断电问题的影响,因此这些表只能作为临时表或缓存使用
特点:内存存储,hash索引
文件:只有xxx.sdi存储表结构信息
9.数据库存储过程
存储过程也是一组数据集。为了完成特定的功能,存储在数据库中,编译后永久有效,存储过程的名称和参数。(存储过程包括带参数和无参数)
有系统存储过程、用户自定义存储等。
存储过程具有增强SQL语言的功能和灵活性,执行速度快,存储过程创建后可在程序中多次调用。
10.索引
优点:可以加快查询效率;通过创建独特索引,可以保证数据库表中每一行数据的独特性。
缺点:创建和维护索引需要时间,增加和删除也会降低执行效率;需要存储物理文件,消耗空间
底层数据结构:hash表(key-value键值对的集合)和B树&B+树
11.sql语句的执行过程
首先,通过连接层查询用户权限;
然后检查缓存是否已经实施,如果有直接返回结果,否则继续下一步;
然后分析SQL的语法和词法是否正确;
然后优化器将执行sql的顺序,并选择 优化哪个索引;
然后执行器操作引擎,返回结果;
最后,存储引擎存储数据,并提供读写界面
12.sql语句优化
(1)插入数据
insert优化-批量插入(最好是500-1000条);手动提交事务 start transaction insert into 语句 commit;插入主键顺序 按照插入主键顺序 速度比乱序插入快
大量插入数据 使用load指令,可以将本地文件加载到数据库中(感觉navicat导入本地txt/excel等文件加载到数据库底部)
(2)主键优化
设计原则:减少主键长度;插入时,尽量选择顺序插入,选择使用自增主键;
尽量不要使用UUID作为主键;业务操作时,避免修改主键。
(3)order by优化
尽量按有序索引顺序扫描,即using index,无需额外排序,效率高。
尽量使用覆盖索引。
根据排序字段建立合适的索引,多字段排序时,也遵循最左前缀规则。
多字段排序是,一升一降,在创建联合索引时要注意规则。
如果using不可避免地出现 filesort,当大量数据排序时,排序可以适当增加, 缓冲区大小(默认为256k)。
(4)group by优化
在分组操作中,可以通过索引来提高效率,最左前缀法则也应该用于索引。
(5)limit优化:
思路:通过创建覆盖索引和子查询进行优化。
(6)count优化
思路:自己记数,用no-sql数据库记数。
尽量使用count(*)数据库优化排序。count(字段)
(7)update优化
根据索引字段更新数据,避免将行锁升级为表锁,削弱并行效率。
13.redis
(1)为什么要使用redis缓存数据库?
减轻数据库的压力。redis的吞吐量为1000级,mysql的吞吐量为1000级。当要求并发量高时,数据库将停机;
改善用户体验。redis使用计算机内存,读取速度快,而mysql数据库使用的计算机磁盘读取速度必须慢;
提高系统并发量。缓存并发量大于数据库并发量。
redis缓存可以分担部分请求,改善整个系统的并发性。
除了缓存,redis还可以做分布式锁、消息队列、限流、复杂的业务场景(如通过bitmap统计活跃用户,通过sorted) set维护排名)
(2)redis常用的数据结构:
五种基础:List、set(集合)、hash(散列)、String、zset(有序集合)
三种特殊:Bitmap(位存储)、HyperLogLogs(基数统计)、Geospatial(地理位置)
redis的过期数据删除策略:惰性删除(key取出时只会过期检查)和定期删除(每隔一段时间取一批key删除过期key)
(3) redis内存淘汰机制:
从设置过期时间的数据集中,a.选择最近最少使用的淘汰;b.选择不常用的淘汰;c.随机淘汰;d.淘汰即将过期的选择
从所有数据集中,a.选择最近最少使用的淘汰;b.选择不常用的淘汰;c.淘汰随机选择数据。
(4) redis持久机制:RDB(快照)和AOF(只添加文件)
RDB:redis通过创建快照获得存储内存中数据在某个时间点的副本。redis备份快照,将快照复制到其他服务器(redis主要从结构上提高性能)
RDB备份过程:
redis创建了一个fork子过程来构建一个临时存储文件,并在持久过程结束后更换上一个持久文件。
这个过程使用了写时复制技术。主流程不进行IO操作,所以速度要快。缺点是最后一次持久操作可能会丢失(如果停机,
但是,如果不能达到可持续操作的时间限制,则最后一次数据将丢失)
AOF:每个命令都会更改redis数据,redis将命令写入内存缓存,然后根据配置决定何时同步到硬盘AOF文件。
持续AOF配置有三种方式:每次修改都会写AOF文件(always);每秒同步一次(everysec);让操作系统决定何时同步(no)
AOF重写:
由于AOF采用了额外的文件方法,为了避免文件越来越大,增加了重写机制,当最后一个重写文件超过设定的阈值时,redis将启动AOF内容压缩,只保留可以恢复数据的最小指令,小于原AOF文件。
重写过程:这里的重写是通过直接读取数据库中的键值来实现的。重写文件将创建一个新的子过程, 然而,由于在创建子的过程中也会有写作操作,因此在执行bgrewriteaof时,将维护AOF重写缓冲区,记录创建子过程中的所有写作操作,然后添加到新的AOF文件的末尾。最后,用新的AOF文件代替旧的AOF文件。
如果RDB和AOF同时打开,默认读取AOF数据。
(5) redis事务:不支持原子性
redis 缓存穿透、缓存击穿、缓存雪崩:
缓存穿透:用户查询的id在缓存中找不到,或恶意者故意发起访问不存在的id请求。(也就是说,每次请求都要求查询数据库,但每次都找不到数据库,会造成数据库压力过大,挂断)。用验证参数过滤部分恶意;用布隆过滤器存储在bitmap位置,用hash值计算要求的id是否存在于数据库中。如果没有,请直接拒绝访问。
缓存击穿:当大量用户同时要求访问某个数据时,数据正好过期无效,那么该请求的压力就会堆积在数据库中,数据库就会挂断。(比如双十一秒杀)可以通过锁定的方式控制,同时只允许一个ID要求;也可以自动续期,在即将到期的时候给key续期;缓存也可以设置为无效。
缓存雪崩:大量流行缓存同时失效,请求压力在数据库上,导致数据库停机;过期时间加随机数,避免大量缓存数据同时失效;Redis集群和流量限制(避免同时处理大量请求)
(6) 分布式锁:
问题描述:由于不同机器上分布式集群系统的多线程和多过程,原单机部署下的并发控制锁策略失效,需要跨JVM的互斥机制来控制共享资源的访问,即分布式锁需要解决的问题。
setnx:通过这个命令试图获得锁,未获得锁的线程将继续等待尝试。
set key ex 3000nx:设置过期时间,自动释放锁,解决业务异常导致锁无法释放的问题。
但是,当业务运行超过期限时,会开启监控线程,增加业务运行时间,知道运行结束,释放锁。
uuid:设置uuid,释放前获取此值,判断自己的锁是否,防止误删锁。
Lua脚本:确保删除锁的原子性。
三种情况:(1)使用setnx上锁,通过del释放锁;(2)如果锁没有释放,设置key过期时间,自动释放锁;
(3)上锁后突然出现异常,无法设置过期时间,上锁时同时设置过期时间 (set users 10 nx ex 12))
框架的后端开发
1.Spring
介绍及优点:
轻量级Javaspring Spring开发框架的核心是控制反转(IoC)和面向切面(AOP)
(1)解耦方便,开发简化 (高内聚低耦合)
Spring是一个大工厂(容器),可以将所有对象的创建和依赖性交给Spring容器管理
(2)支持AOP
Spring提供切面编程,可以方便地实现程序的权限拦截、运行监控等功能
(3)方便各种优秀框架的集成
Spring不排斥各种优秀的开源框架
2.SpringIOC:控制反转
IoC 控制反转的核心思想是将手动创建对象的控制权交给Spring进行管理。
Spring作为一个容器,负责生成bean的例子和注入bean的依赖
三种注入方法:
注入结构方法,注入setter方法,注入注入
3.SrpingAOP:
通过动态代理,Aop面向切面编程,
对类功能进行无限增强,不改变原始代码。
主要功能包括:添加日志、事务、权限
其特点是:降低模块之间的耦合度,提高可维护性
4.SpringMVC
介绍springMVC,谈谈它的优点
springmvc是一个基于MVC的轻量级Web框架,可以完成前后台的交互
Model 业务层 = Service层 + Dao层
View 显示层 表现层 前台的页面性能(jsp页面)
controller 控制层 前台页面与后台代码之间的数据交互(Servlet)
优点:耦合性低,与Spring框架集成,简化JSP开发,支持Restful风格
5.注解
注释说明程序,给计算机看。JDk 1.5后的新特性可以在包、类、字段、方法等方面声明
在局部变量、方法参数等前面,
用来解释和注释这些元素
功能:安全考虑 减少内存泄漏 减少程序员的工作量。
6.MyBatis
ORM框架 DAO层
对象关系映射框架以面对对象的形式完成数据库的操作
类 --> 表
属性 --> 字段
实例 --> 记录
MyBatis中#和KateX parse error: Expected ‘EOF’, got ‘#’ at position 5: 区别? #{}预编译处理,
防止SQL注…{}是字符串替换。
7. Spring、SpringMVC、SpringBoot之间的关系?
Spring包含许多模块,如IOC、AOP、Core(提供IOC依赖注入的功能)
SpringMVC是Spring非常重要的模块,其核心思想是分离业务逻辑、显示和数据来组织代码。
SpringBoot是为了简化XML、java 显示配置,真正做到开箱即用
8.SpringBean是什么?
事实上,SpringIOC容器中管理的对象实际上是类对象,通过id、class属性配置
Bean的注释中有一个类声明:
@Component:不知道用这个的时候用哪一层。
@Service:对应服务层
@Controller:对应SpringMVC控制层
@Repository:对应持久层DAO层,主要用于数据库相关操作
9. @Autowired和@resource的区别
@Autowired是spring提供的注释,通过bytype注入,@resource是JDK提供的注释,
通过byname注入
10. SpringMVC工作原理
在客户端发送请求后,被DispatchServlet拦截请求;
根据要求信息,Dispatchservlet调用Handlermapping;
Dispatchservlet调用Handleradapter适应执行Handler(即Controller控制器);
Handler完成用户请求处理后,将ModelAndview对象返回Dispatchservlet;
Viewresolver将根据逻辑View寻找实际View;
Dispatchservlet将返回的model传递给View(视图渲染);
将View返回浏览器。
11. Spring框架中使用了哪些设计模式?
工厂设计模式:BeanFactory、创建bean对象的Applicationcontext;
单例设计模式:bean默认为单例;
代理设计模式:实现SpringAOP功能;
适配器设计模式:SpringMVC适用于适配器模式的Controler。
12. Spring事务
(1)管理事务的方式:
编程事务:手动管理事务,很少使用
声明事务:在XML配置文件中配置或基于注释@Transactional
(2)Spring事务的隔离级别:
默认,读未提交,读已提交(解决脏读)、可重复读(解决脏读,不能重复读)、可序列化
(解决所有脏读、不重复读、幻读)
13.SpringBoot有哪些优势/为什么要使用SpringBoot?
减少开发和测试时间;
避免大量maven导入与各种版本发生冲突;
没有单独的web服务器需求,这意味着不再需要启动tomcat,直接启动主程序;
没有web,需要更少的配置.xml文件。只需@Configuration注释类,@Bean注释方法,
Spring将自动加载对象,甚至将@Autowired自动加载到bean中
其他
1.加密算法分为对称和非对称。
DES:数据dao加密标准,速度快,适用于大量数据的加密
3DES:基于DES,用三个不同的密钥加密一块数据,强度更高;
AES:高级加密标准,速度快,安全水平高。
如何解决页面加载缓慢的问题?
有两个原因和解决方案:页面记录速度慢:
(1)数据请求界面返回缓慢。
创建要求的接口作为定时任务,时间间隔可根据接口压力测量进行调整;
将定期任务请求的数据写入缓存中,例如使用redis缓存数据库。在前端发送请求后,首先从缓存中获取数据。如果没有,请直接访问接口并更新缓存。
(2)页面渲染速度慢。
拆开页面上的数据接口,异步要求;
适配页面,先渲染获得的数据;
在填写之前,可以先渲染页面,请求数据。