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

SpringBoot整合Quartz,实现数据库方式执行定时任务

springboot整合quartz,实现数据库方式执行定时任务。把定时任务信息存进数据库,项目启动后自动执行定时任务。

1.引入依赖包:

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

2.配置yml:

spring:
  # quartz
  quartz:
    #相关属性配置
    properties:
      org:
        quartz:
          scheduler:
            instanceName: DefaultQuartzScheduler
            instanceId: AUTO
          jobStore:
            class: org.quartz.impl.jdbcjobstore.JobStoreTX
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
            tablePrefix: QRTZ_
            isClustered: false
            clusterCheckinInterval: 10000
            #以指示JDBCJobStore将JobDataMaps中的所有值都作为字符串,因此可以作为名称
            #- 值对存储而不是在BLOB列中以其序列化形式存储更多复杂的对象。
            #从长远来看,这是更安全的,因为您避免了将非String类序列化为BLOB的类版本问题。
            useProperties: false
          threadPool:
            class: org.quartz.simpl.SimpleThreadPool
            threadCount: 10
            threadPriority: 5
            threadsInheritContextClassLoaderOfInitializingThread: true
 
    # 数据库方式
    job-store-type: jdbc
 
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/quartz_test?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
 
server:
  port: 8080

3.后端编码:

controller层:

@Controller
@RequestMapping(value = "/job")
@Slf4j
public class JobController {
 
    @Autowired
    private IJobService jobService;
 
 
 
    @PostMapping(value = "/add")
    @ResponseBody
    public Result addJob(@RequestBody JobInfo jobInfo) {
        return jobService.addJob(jobInfo);
    }
 
    @GetMapping(value = "/getAllJobs")
    @ResponseBody
    public List<JobInfo> getAllJobs() {
        return jobService.getAllJobs();
    }
 
    @PostMapping(value = "/pause")
    @ResponseBody
    public Result pauseJob(String name, String group) {
        return jobService.pauseJob(name,group) ? Result.success() : Result.error();
    }
 
    @PostMapping(value = "/resume")
    @ResponseBody
    public Result resumeJob(String name, String group) {
        return jobService.resumeJob(name,group) ? Result.success() : Result.error();
    }
 
    @PostMapping(value = "/reschedule")
    @ResponseBody
    public Result reScheduleJob(String name, String group, String cron) {
        return jobService.reScheduleJob(name, group, cron) ? Result.success() : Result.error();
    }
 
    @PostMapping(value = "/delete")
    @ResponseBody
    public Result deleteJob(String name, String group) {
        return jobService.deleteJob(name,group) ? Result.success() : Result.error();
    }
 
    /**
     * @description 校验是否是合法cron表达式
     * @param  cron
     * @return  com.cxh.cxhquartz.common.Result
     **/
    @PostMapping(value = "/checkCron")
    @ResponseBody
    public Result checkCron(String cron) {
        boolean valide = false;
        try {
            valide = CronExpression.isValidExpression(cron);
        }catch (Exception e){
            log.error(e.getMessage());
        }
        return valide ? Result.success() : Result.error();
    }
}

service层:

public interface IJobService {
 
    /**
     * @description 查询所有任务
     * @param
     * @return  java.util.List<com.cxh.cxhquartz.quartz.JobInfo>
     **/
     List<JobInfo> getAllJobs();
    /**
     * @description 恢复任务
     * @param  jobName
     * @Param  jobGroup
     * @return  boolean
     **/
     boolean resumeJob(String jobName,String jobGroup);
    /**
     * @description 停止任务
     * @param  jobName
     * @Param  jobGroup
     * @return  boolean
     **/
     boolean pauseJob(String jobName,String jobGroup);
    /**
     * @description 修改任务执行周期表达式
     * @param  jobName
     * @Param  jobGroup
     * @Param  cronExpression
     * @return  boolean
     **/
     boolean reScheduleJob(String jobName,String jobGroup,String cronExpression);
    /**
     * @description 删除任务
     * @param  jobName
     * @Param  jobGroup
     * @return  boolean
     **/
     boolean deleteJob(String jobName,String jobGroup);
    /**
     * @description 新增任务
     * @param  jobInfo
     * @return  int
     **/
    Result addJob(JobInfo jobInfo);
     /**
      * @description 判断任务是否存在
      * @param  jobKey
      * @return  int
      **/
     int isJobExist(JobKey jobKey);
}

@Slf4j
@Service
public class JobServiceImpl implements IJobService {
 
    @Autowired
    private Scheduler scheduler;
 
    @Override
    public List<JobInfo> getAllJobs() {
        List<JobInfo> jobInfoList = new ArrayList<>();
        try{
            List<String> groupList = scheduler.getJobGroupNames();
            for(String group : groupList){
                GroupMatcher<JobKey> groupMatcher = GroupMatcher.groupEquals(group);
                Set<JobKey> jobKeySet = scheduler.getJobKeys(groupMatcher);
                for(JobKey jobKey : jobKeySet){
                    JobInfo jobInfo = new JobInfo();
                    JobDetail jobDetail = scheduler.getJobDetail(jobKey);
                    jobInfo.setJobname(jobKey.getName());
                    jobInfo.setJobgroup(jobKey.getGroup());
                    jobInfo.setJobclassname(jobDetail.getJobClass().getName());
                    Trigger trigger = scheduler.getTrigger(TriggerKey.triggerKey(jobKey.getName(), jobKey.getGroup()));
                    if(trigger != null){
                        Trigger.TriggerState state = scheduler.getTriggerState(TriggerKey.triggerKey(jobKey.getName(), jobKey.getGroup()));
                        jobInfo.setTriggername(jobKey.getName());
                        jobInfo.setTriggergroup(jobKey.getGroup());
                        try{
                            CronTrigger cronTrigger = (CronTrigger) trigger;
                            jobInfo.setCronexpression(cronTrigger.getCronExpression());
                        } catch (Exception e){
                            log.error(e.getMessage());
                        }
                        if(trigger.getNextFireTime() != null){
                            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                            jobInfo.setNextfiretime(sdf.format(trigger.getNextFireTime()));
                        }
                        jobInfo.setDescription(jobDetail.getDescription());
                        jobInfo.setState(state.name());
                        jobInfo.setId(UUID.randomUUID().toString());
                        jobInfoList.add(jobInfo);
                    } else {
                        jobInfo.setState("OVER");
                        jobInfo.setId(UUID.randomUUID().toString());
                        jobInfoList.add(jobInfo);
                    }
                }
            }
        } catch (Exception e){
            log.error(e.getMessage());
        }
        return jobInfoList;
    }
 
    @Override
    public boolean resumeJob(String jobName, String jobGroup) {
        boolean flag = true;
        try{
            scheduler.resumeJob(JobKey.jobKey(jobName, jobGroup));
        } catch (Exception e){
            flag = false;
            log.error(e.getMessage());
        }
        return flag;
    }
 
    @Override
    public boolean pauseJob(String jobName, String jobGroup) {
        boolean flag = true;
        try{
            scheduler.pauseJob(JobKey.jobKey(jobName, jobGroup));
        } catch (Exception e){
            flag = false;
            log.error(e.getMessage());
        }
        return flag;
    }
 
    @Override
    public boolean reScheduleJob(String jobName, String jobGroup, String cronExpression) {
        boolean flag = true;
        try{
           Trigger.TriggerState state = scheduler.getTriggerState(TriggerKey.triggerKey(jobName, jobGroup));
           CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(TriggerKey.triggerKey(jobName, jobGroup));
           if(cronTrigger != null && cronExpression != null && !cronTrigger.getCronExpression().equals(cronExpression)){
               CronTrigger cronTriggerNew = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup).withSchedule(CronScheduleBuilder.cronSchedule(cronExpression)).build();
               scheduler.rescheduleJob(TriggerKey.triggerKey(jobName, jobGroup), cronTriggerNew);
               if(state.name().equals("PAUSED")){
                   this.pauseJob(jobName, jobGroup);
               }
           }
        } catch (Exception e){
            flag = false;
            log.error(e.getMessage());
        }
        return flag;
    }
 
    @Override
    public boolean deleteJob(String jobName, String jobGroup) {
        boolean flag = true;
        try{
            List<? extends Trigger> triggerList = scheduler.getTriggersOfJob(JobKey.jobKey(jobName, jobGroup));
            if(triggerList.size() > 0){
                if(!"PAUSED".equals(scheduler.getTriggerState(TriggerKey.triggerKey(jobName, jobGroup)).name())){
                    scheduler.pauseTrigger(TriggerKey.triggerKey(jobName, jobGroup));
                }
                scheduler.unscheduleJob(TriggerKey.triggerKey(jobName, jobGroup));
            }
            scheduler.deleteJob(JobKey.jobKey(jobName, jobGroup));
        } catch (Exception e){
            flag = false;
            log.error(e.getMessage());
        }
        return flag;
    }
 
    @Override
    public Result addJob(JobInfo jobInfo) {
        int isJobExist = this.isJobExist(JobKey.jobKey(jobInfo.getJobname(), jobInfo.getJobgroup()));
        if(isJobExist == 1){
            return Result.error("任务已存在!");
        }
        try{
            JobDetail jobDetail = null;
            if(isJobExist == 0){
                jobDetail = scheduler.getJobDetail(JobKey.jobKey(jobInfo.getJobname(), jobInfo.getJobgroup()));
            } else if(isJobExist == -1){
                jobDetail = JobBuilder.newJob(
                        (Class<? extends Job>) Class.forName(jobInfo.getJobclassname()))
                        .withIdentity(jobInfo.getJobname(), jobInfo.getJobgroup())
                        .withDescription(jobInfo.getDescription())
                        .storeDurably().build();
            }
            //如果jobInfo的cron表达式为空,则创建常规任务,反之创建周期任务
            if(jobInfo.getCronexpression() != null){
                CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(jobInfo.getTriggername(), jobInfo.getTriggergroup())
                        .withSchedule(CronScheduleBuilder.cronSchedule(jobInfo.getCronexpression()))
                        .build();
                scheduler.scheduleJob(jobDetail, cronTrigger);
 
            } else {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                Trigger trigger = TriggerBuilder.newTrigger().withIdentity(jobInfo.getJobname(), jobInfo.getJobgroup())
                        .startAt(sdf.parse(jobInfo.getNextfiretime()))
                        .withSchedule(SimpleScheduleBuilder.simpleSchedule().withRepeatCount(0))
                        .build();
                scheduler.scheduleJob(jobDetail, trigger);
            }
        } catch (SchedulerException e){
            log.error(e.getMessage());
            return Result.error("添加任务失败!");
        } catch (ParseException e){
            log.error(e.getMessage());
            return Result.error("时间转换出错!");
        } catch (Exception e){
            log.error(e.getMessage());
            return Result.error(e.getMessage());
        }
        return Result.success("添加任务成功!");
    }
 
    @Override
    public int isJobExist(JobKey jobKey) {
       int flag = -1;
       try{
           JobDetail jobDetail = scheduler.getJobDetail(jobKey);
           List<? extends  Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
           if(jobDetail != null && triggers.size() > 0){
               flag = 1;
           } else if(jobDetail != null && triggers.size() == 0){
                flag = 0;
           }
       } catch (Exception e){
           flag = -1;
           log.error(e.getMessage());
       }
       return flag;
    }
}

entity层:

@Data
public class JobInfo implements Serializable {
    private String id;
    private String jobname;
    private String jobgroup;
    private String jobclassname;
    private String triggername;
    private String triggergroup;
    private String cronexpression;
    private String description;
    private String prefiretime;
    private String nextfiretime;
    private String state;
}

定时任务:

@Slf4j
public class DemoJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        JobDetail jobDetail = jobExecutionContext.getJobDetail();
        log.info("任务名:" + jobDetail.getKey().getName() + ",组名:" +
                jobDetail.getKey().getGroup() + "------执行的定时任务工作内容!");
    }
}

4.创建数据库表:quartz保存定时任务信息表

  5.运行项目,打开postman发起请求:

 6.运行结果:

相关文章:

SpringBoot整合Quartz,实现数据库方式执行定时任务

springboot整合quartz&#xff0c;实现数据库方式执行定时任务。把定时任务信息存进数据库&#xff0c;项目启动后自动执行定时任务。 1.引入依赖包&#xff1a; <dependency> <groupId>org.springframework.boot</groupId> <ar…...

java中多个list怎么用List表示?

如果你有多个List对象&#xff0c;想要将它们合并成一个List对象&#xff0c;可以使用addAll()方法来实现。addAll()方法将会把一个List中的元素逐个添加到另一个List中。 以下是一个示例&#xff0c;展示了如何将多个List对象合并为一个List对象&#xff1a; import java.ut…...

postgresql 数据排序

postgresql 常见操作 排序总结 排序 -- 排序的时候null是最大的值(看一下) select employee_id,manager_id from employeesorder by manager_id desc;-- nulls first使null值排在第一位 select employee_id,manager_id from employeesorder by manager_id nulls first;-- null…...

虚拟机 net、桥接、主机三种网络模式寻根问底

虚拟机使用物理主机上的网络适配器直接连接到物理网络中。 这意味着虚拟机就像是通过网线直接连接到路由器一样,成为物理网络中的一个独立设备。 虚拟机可以获取一个永久的IP地址,通过DHCP或手动设置。 虚拟机和物理主机都可以访问对方以及公共网络中的其他设备,比如文件服务…...

python代码——批量将PPT转换成长图

语言&#xff1a;python 3 用法&#xff1a;点击运行后&#xff0c;弹出窗口&#xff0c;选择文件夹&#xff0c;程序运行会将文件夹内的所有PPT文件全部转换成PPT长图&#xff0c;图片名称与PPT文件名称相同&#xff0c;保存位置相同。 如运行中报错&#xff0c;需要自行根据…...

C++信息学奥赛2046:【例5.15】替换字母

这段代码的功能是对输入的字符串进行处理&#xff0c;将字符串中的字符 a 替换为字符 b 后输出结果。 #include<bits/stdc.h> using namespace std; int main() {string s; // 定义字符串变量s&#xff0c;用来存储输入的字符串char a, b; // 定义字符变量a和b&#xff…...

每天一道leetcode:1306. 跳跃游戏 III(图论中等广度优先遍历)

今日份题目&#xff1a; 这里有一个非负整数数组 arr&#xff0c;你最开始位于该数组的起始下标 start 处。当你位于下标 i 处时&#xff0c;你可以跳到 i arr[i] 或者 i - arr[i]。 请你判断自己是否能够跳到对应元素值为 0 的 **任一** 下标处。 注意&#xff0c;不管是什…...

76参考链接

参考链接 官方文件综合介绍[let 和 const](https://es6.ruanyifeng.com/#docs/reference#let 和 const)解构赋值字符串正则数值数组函数对象Symbol[Set 和 Map](https://es6.ruanyifeng.com/#docs/reference#Set 和 Map)[Proxy 和 Reflect](https://es6.ruanyifeng.com/#docs/…...

浅析Linux SCSI子系统:调试方法

文章目录 SCSI日志调试功能scsi_logging_level调整SCSI日志等级 SCSI trace events使能SCSI trace events方式一&#xff1a;通过set_event接口方式二&#xff1a;通过enable 跟踪trace信息 相关参考 SCSI日志调试功能 SCSI子系统支持内核选项CONFIG_SCSI_LOGGING配置日志调试…...

【Unity3D】水面特效

1 前言 水波特效 中通过屏幕后处理实现了环形水波效果&#xff0c;本文通过 Shader Graph 实现了模拟水面特效&#xff0c;包含以下特效细节。 深水区和浅水区颜色差异&#xff1b;水面有波纹&#xff0c;并且在移动&#xff1b;水面起伏波动&#xff1b;水面边缘有水泡&#…...

CSS中的flex布局详细讲解

Flex 布局 Flex 布局是一种现代的 CSS 布局模型&#xff0c;用于实现灵活的盒子布局。它提供了强大的布局能力&#xff0c;使得元素可以自动调整大小、对齐和分布&#xff0c;适用于构建响应式和可伸缩的布局。 Flex 布局使用 flex 容器和 flex 项目的概念。容器是一个父元素…...

Python功能制作之简单的音乐播放器

需要导入的库&#xff1a; pip install PyQt5 源码&#xff1a; import os from PyQt5.QtCore import Qt, QUrl from PyQt5.QtGui import QIcon, QPixmap from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent from PyQt5.QtWidgets import QApplication, QMainWind…...

GAN生成对抗模型根据minist数据集生成手写数字图片

文章目录 1.项目介绍2相关网站3具体的代码及结果导入工具包设置超参数定义优化器&#xff0c;以及损失函数训练时的迭代过程训练结果的展示 1.项目介绍 通过用minist数据集进行训练&#xff0c;得到一个GAN模型&#xff0c;可以生成与minist数据集类似的图片。 GAN是一种生成模…...

【K8S源码之Pod漂移】整体概况分析 controller-manager 中的 nodelifecycle controller(Pod的驱逐)

参考 k8s 污点驱逐详解-源码分析 - 掘金 k8s驱逐篇(5)-kube-controller-manager驱逐 - 良凯尔 - 博客园 k8s驱逐篇(6)-kube-controller-manager驱逐-NodeLifecycleController源码分析 - 良凯尔 - 博客园 k8s驱逐篇(7)-kube-controller-manager驱逐-taintManager源码分析 - 良…...

[保研/考研机试] KY212 二叉树遍历 华中科技大学复试上机题 C++实现

题目链接&#xff1a; 二叉树遍历_牛客题霸_牛客网二叉树的前序、中序、后序遍历的定义&#xff1a; 前序遍历&#xff1a;对任一子树&#xff0c;先访问根&#xff0c;然后遍历其左子树&#xff0c;最。题目来自【牛客题霸】https://www.nowcoder.com/share/jump/43719512169…...

CSS笔记

介绍 CSS导入方式 三种方法都将文字设置成了红色 CSS选择器 元素选择器 id选择器 图中div将颜色控制为红色&#xff0c;#name将颜色控制为蓝色&#xff0c;谁控制的范围最小&#xff0c;谁就生效&#xff0c;所以第二个div是蓝色的。id属性值要唯一&#xff0c;否则报错。 clas…...

链栈Link-Stack

0、节点结构体定义 typedef struct SNode{int data;struct SNode *next; } SNode, *LinkStack; 1、初始化 bool InitStack(LinkStack &S) //S为栈顶指针&#xff08;存数据的头节点&#xff09; {S NULL;return true; } 2、入栈 bool Push(LinkStack &S, int e) {…...

Ubuntu 20系统WIFI设置静态IP地址,以及断连问题

​最近工作需要购置了一台GPU机器&#xff0c;然后搭建了深度学习的运行环境&#xff0c;在工作中将这台机器当做深度学习的服务器来使用&#xff0c;前期已经配置好多用户以及基础环境。但最近通过xshell连接总是不间断的出现断连现象。 补充一点&#xff0c;Ubuntu系统中与网…...

(一)idea连接GitHub的全部流程(注册GitHub、idea集成GitHub、增加合作伙伴、跨团队合作、分支操作)

&#xff08;二&#xff09;Git在公司中团队内合作和跨团队合作和分支操作的全部流程&#xff08;一篇就够&#xff09;https://blog.csdn.net/m0_65992672/article/details/132336481 4.1、简介 Git是一个免费的、开源的*分布式**版本控制**系统*&#xff0c;可以快速高效地…...

-bash: java: command not found笔记

文章目录 场景解决方案找java的方法find命令进行查找根据java进程找寻具体位置 场景 linux系统执行java命令时报错&#xff1a; -bash: java: command not found。 解决方案 可能是没有安装java(这种情况比较少)或者安装了java但是没有设置环境变量(一般是这种情况)。 找ja…...

【Python】 -- 趣味代码 - 小恐龙游戏

文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...

工业安全零事故的智能守护者:一体化AI智能安防平台

前言&#xff1a; 通过AI视觉技术&#xff0c;为船厂提供全面的安全监控解决方案&#xff0c;涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面&#xff0c;能够实现对应负责人反馈机制&#xff0c;并最终实现数据的统计报表。提升船厂…...

TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案

一、TRS收益互换的本质与业务逻辑 &#xff08;一&#xff09;概念解析 TRS&#xff08;Total Return Swap&#xff09;收益互换是一种金融衍生工具&#xff0c;指交易双方约定在未来一定期限内&#xff0c;基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作

一、上下文切换 即使单核CPU也可以进行多线程执行代码&#xff0c;CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短&#xff0c;所以CPU会不断地切换线程执行&#xff0c;从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

【JavaWeb】Docker项目部署

引言 之前学习了Linux操作系统的常见命令&#xff0c;在Linux上安装软件&#xff0c;以及如何在Linux上部署一个单体项目&#xff0c;大多数同学都会有相同的感受&#xff0c;那就是麻烦。 核心体现在三点&#xff1a; 命令太多了&#xff0c;记不住 软件安装包名字复杂&…...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...

网站指纹识别

网站指纹识别 网站的最基本组成&#xff1a;服务器&#xff08;操作系统&#xff09;、中间件&#xff08;web容器&#xff09;、脚本语言、数据厍 为什么要了解这些&#xff1f;举个例子&#xff1a;发现了一个文件读取漏洞&#xff0c;我们需要读/etc/passwd&#xff0c;如…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)

漏洞概览 漏洞名称&#xff1a;Apache Flink REST API 任意文件读取漏洞CVE编号&#xff1a;CVE-2020-17519CVSS评分&#xff1a;7.5影响版本&#xff1a;Apache Flink 1.11.0、1.11.1、1.11.2修复版本&#xff1a;≥ 1.11.3 或 ≥ 1.12.0漏洞类型&#xff1a;路径遍历&#x…...

JavaScript 数据类型详解

JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型&#xff08;Primitive&#xff09; 和 对象类型&#xff08;Object&#xff09; 两大类&#xff0c;共 8 种&#xff08;ES11&#xff09;&#xff1a; 一、原始类型&#xff08;7种&#xff09; 1. undefined 定…...