从0到1搭建Springboot整合Quartz定时任务框架(保姆级教学+Gitee源码)

news2024/12/27 13:33:46

前言:最近学习了目前主流的若依框架,这是一个非常优秀的开源项目,故此我这边把它的源码全部剖析了一遍,简化了它的框架,我会通过这篇博客详细讲解我是如何进行推敲和从0到1搭建这个项目的流程。

目录

一、Quartz简介

二、项目整体结构图

三、代码实现

3.1、导入pom.xml依赖

3.2、定时任务实体类

3.3、撰写定时任务调度信息接口和实现类

3.4、撰写定时任务工具类

3.5、撰写AbstractQuartzJob抽象类

3.6、撰写开启/禁止并发执行任务处理类

3.7、撰写执行定时任务方法类和获取Bean的工具类

四、运行测试

4.1、撰写任务一

4.2、撰写任务二 

4.3、启动

五、Gitee源码地址

六、总结 


一、Quartz简介

Quartz是一个完全由Java编写的开源作业调度框架,在Java应用程序中进行作业调度提供了强大功能,以下是Quartz的四个核心概念。

1、Job(接口):它只有一个execute方法需要被重写,重写的内容就是咱们需要执行的具体内容。

2、JobDetail(调度信息):表示一个具体的可执行的调度程序,Job是这个可执行调度程序中所需要执行的具体内容,另外JobDetail还包含了这个任务的调度方案和策略。

3、Trigger(触发器):代表一个调度参数的配置,动态去执行咱们的定时任务。

4、Scheduler(任务调度器):Scheduler就是任务调度控制器,需要把JobDetail和Trigger注册到Schedule中,才可以执行。

咱们废话不多说,直接上代码!

二、项目整体结构图

这边我先把我整个从0到1搭建好的框架先展示出来。

三、代码实现

这边我会详细阐述这个项目是如何完整搭建起来的,跟着文章的步骤一步步走就可以了。

3.1、导入pom.xml依赖

<dependencies>

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

        <!--quartz定时任务依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>

        <!--常用工具类 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

        <!--lombok依赖 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        
    </dependencies>

3.2、定时任务实体类

package com.ithuang.quartz.domain;

import lombok.Data;

/**
 * 定时任务调度表 sys_job
 *
 * @author HTT
 */
@Data
public class SysJob
{

    /**
     * 定时任务ID
     */
    private String jobId;

    /**
     * 定时任务名称
     */
    private String jobName;

    /**
     * 定时任务组
     */
    private String jobGroup;

    /**
     * 目标bean名
     */
    private String beanTarget;

    /**
     * 目标bean的方法名
     */
    private String beanMethodTarget;

    /**
     * 执行表达式
     */
    private String cronExpression;

    /**
     * 是否并发
     */
    private String concurrent;


}

3.3、撰写定时任务调度信息接口和实现类

撰写定时任务调度的接口,主要分为初始化、暂停,恢复和删除定时任务,代码如下,这个不多说,主要详细讲解一下它的实现类。

package com.ithuang.quartz.service;

import com.ithuang.quartz.domain.SysJob;
import org.quartz.SchedulerException;

import java.lang.reflect.InvocationTargetException;

/**
 * 定时任务调度信息
 * @author HTT
 */
public interface SysJobService {

    /**
     * 项目启动时,初始化定时器
     */
    void init() throws SchedulerException, NoSuchMethodException, InvocationTargetException, IllegalAccessException;

    /**
     * 暂停任务
     *
     * @param job 调度信息
     * @return 结果
     */
    public int pauseJob(SysJob job) throws SchedulerException;

    /**
     * 恢复任务
     *
     * @param job 调度信息
     * @return 结果
     */
    public int resumeJob(SysJob job) throws SchedulerException;

    /**
     * 删除任务后,所对应的trigger也将被删除
     *
     * @param job 调度信息
     * @return 结果
     */
    public int deleteJob(SysJob job) throws SchedulerException;

}

如下代码是上述接口的实现类:

1、首先initTaskList这个方法我这边做了偷懒,直接写了一个List集合在项目中,正确的配置应该是从数据库去查询一共有哪些定时任务需要程序去执行的,以及对应的配置信息应该如何去执行。

2、其次在init方法中,@PostConstruct这个注解会在项目启动的时候帮我们进行初始化,其次我调用了clear方法进行清空定时任务,最后使用循环获取到需要所有定时任务集合,通过createScheduleJob方法去创建,接下来我就会详细讲解createScheduleJob方法中的代码逻辑是如何实现的。

3、剩下的就是一些通过动态传参去暂停、恢复和删除定时任务的实现方法,比较易懂不再阐述。

package com.ithuang.quartz.service.impl;

import com.ithuang.quartz.domain.SysJob;
import com.ithuang.quartz.service.SysJobService;
import com.ithuang.quartz.utils.ScheduleUtils;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * 定时任务调度服务
 * @author HTT
 */
@Service
public class SysJobServiceImpl implements SysJobService {

    @Resource
    private Scheduler scheduler;

    /**
     * 模拟从数据库获取数据,这边偷懒了
     * @return
     */
    public List<SysJob> initTaskList(){
        List<SysJob> list = new ArrayList<>();
        SysJob job = new SysJob();
        job.setJobId(UUID.randomUUID().toString());
        job.setJobGroup("system");
        job.setConcurrent("0");
        job.setCronExpression("0/5 * * * * ?");
        job.setBeanTarget("task1");
        job.setBeanMethodTarget("handle");
        list.add(job);
        job = new SysJob();
        job.setJobId(UUID.randomUUID().toString());
        job.setJobGroup("system");
        job.setConcurrent("0");
        job.setCronExpression("0/10 * * * * ?");
        job.setBeanTarget("task2");
        job.setBeanMethodTarget("handle");
        list.add(job);
        return list;
    }

    /**
     * 初始化定时任务
     * @throws SchedulerException
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    @PostConstruct
    @Override
    public void init() throws SchedulerException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        scheduler.clear();
        List<SysJob> list = initTaskList();
        for (int i = 0 ; i < list.size() ; i ++){
            SysJob job = list.get(i);
            ScheduleUtils.createScheduleJob(scheduler, job);
        }

    }

    @Override
    public int pauseJob(SysJob job) throws SchedulerException {
        scheduler.pauseJob(JobKey.jobKey(job.getJobId(),job.getJobGroup()));
        return 1;
    }

    @Override
    public int resumeJob(SysJob job) throws SchedulerException {
        scheduler.resumeJob(JobKey.jobKey(job.getJobId(),job.getJobGroup()));
        return 1;
    }

    @Override
    public int deleteJob(SysJob job) throws SchedulerException {
        scheduler.deleteJob(JobKey.jobKey(job.getJobId(),job.getJobGroup()));
        return 1;
    }





}

3.4、撰写定时任务工具类

这边我一共封装了两个方法,getQuartzJobClass这个方法是主要通过sysJob实体类当中会通过动态参数获取任务类并决定当前任务是否并发执行(0代表并发执行,1代表禁止并发执行),最后createScheduleJob方法也就是我们最主要的方法了,它的逻辑总共6个步骤如下:

1、得到任务类(是否并发执行)

2、构建job信息

3、构件表达式调度构建器

4、按新的cronExpression表达式构建一个新的trigger

5、放入参数,运行时的方法可以获取

6、执行调度任务

package com.ithuang.quartz.utils;

import com.ithuang.quartz.domain.QuartzDisallowConcurrentExecution;
import com.ithuang.quartz.domain.QuartzJobExecution;
import com.ithuang.quartz.domain.SysJob;
import org.quartz.*;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;

/**
 * 定时任务工具类
 * @author HTT
 */
@Component
public class ScheduleUtils {

    /**
     * 得到quartz任务类
     *
     * @param sysJob 执行计划
     * @return 具体执行任务类
     */
    private static Class<? extends Job> getQuartzJobClass(SysJob sysJob)
    {
        boolean isConcurrent = "0".equals(sysJob.getConcurrent());
        return isConcurrent ? QuartzJobExecution.class : QuartzDisallowConcurrentExecution.class;
    }

    /**
     * 创建定时任务
     */
    public static void createScheduleJob(Scheduler scheduler, SysJob job) throws SchedulerException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        Class<? extends Job> jobClass = getQuartzJobClass(job);
        // 构建job信息
        String cornExpression = job.getCronExpression();
        JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(job.getJobId(),job.getJobGroup()).build();

        // 表达式调度构建器
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cornExpression);

        // 按新的cronExpression表达式构建一个新的trigger
        CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobId(),job.getJobGroup())
                .withSchedule(cronScheduleBuilder).build();

        // 放入参数,运行时的方法可以获取
        jobDetail.getJobDataMap().put("param", job);

        // 执行调度任务
        scheduler.scheduleJob(jobDetail, trigger);

    }

}

3.5、撰写AbstractQuartzJob抽象类

大家看到这肯定会很好奇上面的getQuartzJobClass方法它是如何动态获取对应的任务类的,这边我们首先定义了一个抽象类并实现了Job接口,同时会重写execute方法,这个方法是定时任务执行的核心方法,其次在这个抽象类当中我们还定义了一个doExecute方法,主要就是为了写我们定时任务的执行逻辑,这边巧妙的用到了Java的设计模式-模板方法模式,关于什么是模板方法模式,可以看我前几期的博客。

package com.ithuang.quartz.domain;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.lang.reflect.InvocationTargetException;

/**
 * 抽象quartz调用
 * 这里我采用了设计模式中的模板方法模式
 * @author HTT
 */
public abstract class AbstractQuartzJob implements Job {

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        try {
            doExecute(jobExecutionContext);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    protected abstract void doExecute(JobExecutionContext jobExecutionContext) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException;
}

3.6、撰写开启/禁止并发执行任务处理类

这边我们继承了上述的抽象类,通过@DisallowConcurrentExecution这个注解来禁止咱们的定时任务的并发执行,如果不加则是默认允许并发执行,最后我们重写了doExecute方法去执行我们定时任务的处理逻辑。

不允许并发执行:

package com.ithuang.quartz.domain;

import com.ithuang.quartz.utils.SpringUtils;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import static com.ithuang.quartz.utils.JobExecuteUtils.executeMethod;


/**
 * 定时任务处理(禁止并发执行)
 * @author HTT
 */
@DisallowConcurrentExecution
public class QuartzDisallowConcurrentExecution extends AbstractQuartzJob
{
    @Override
    protected void doExecute(JobExecutionContext jobExecutionContext) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        executeMethod(jobExecutionContext);
    }
}

允许并发执行:

package com.ithuang.quartz.domain;

import com.ithuang.quartz.utils.JobExecuteUtils;
import com.ithuang.quartz.utils.SpringUtils;
import org.quartz.JobExecutionContext;
import org.springframework.beans.BeanUtils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import static com.ithuang.quartz.utils.JobExecuteUtils.executeMethod;
import static org.springframework.beans.BeanUtils.*;

/**
 * 定时任务处理(允许并发执行)
 * @author HTT
 */
public class QuartzJobExecution extends AbstractQuartzJob
{
    @Override
    protected void doExecute(JobExecutionContext jobExecutionContext) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        executeMethod(jobExecutionContext);
    }
}

3.7、撰写执行定时任务方法类和获取Bean的工具类

这边我们就对上述的doExecute中的方法进行讲解,逻辑如下:

1、我们通过jobExecutionContext.getMergedJobDataMap().get("param");这个方法去获取咱们的SysJob参数信息。

2、默认获取的是Object类型,所以我们需要通过BeanUtils.copyProperties(param,sysJob);这个方法进行实体类的转换和拷贝,注意参数顺序别写反了。

3、然后我们通过SpringUtils封装好的getBean方法,通过拷贝好的实体类中的参数去动态进行获取被Spring托管的Bean。

4、获取到Bean之后通过 bean.getClass().getMethod(sysJob.getBeanMethodTarget());这个代码去获取Bean中需要执行的方法。

5、最后通过method.invoke(bean);进行执行Bean中对应的方法内容。

package com.ithuang.quartz.utils;

import com.ithuang.quartz.domain.SysJob;
import org.quartz.JobExecutionContext;
import org.springframework.beans.BeanUtils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 执行定时任务的方法
 * @author HTT
 */
public class JobExecuteUtils {

    /**
     * 获取bean并执行对应的方法
     * @param jobExecutionContext
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    public static void executeMethod(JobExecutionContext jobExecutionContext) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Object param = jobExecutionContext.getMergedJobDataMap().get("param");
        SysJob sysJob = new SysJob();
        BeanUtils.copyProperties(param,sysJob);
        Object bean = SpringUtils.getBean(sysJob.getBeanTarget());
        Method method = bean.getClass().getMethod(sysJob.getBeanMethodTarget());
        method.invoke(bean);
    }
}

获取Bean的工具类

package com.ithuang.quartz.utils;

import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * 获取Bean的工具类
 * @author HTT
 */
@Component
public final class SpringUtils implements BeanFactoryPostProcessor
{
    /** Spring应用上下文环境 */
    private static ConfigurableListableBeanFactory beanFactory;


    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
    {
        SpringUtils.beanFactory = beanFactory;
    }

    /**
     * 获取对象
     *
     * @param name
     * @return Object 一个以所给名字注册的bean的实例
     * @throws BeansException
     *
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) throws BeansException
    {
        return (T) beanFactory.getBean(name);
    }


}

四、运行测试

以上就是整个定时任务框架实现的核心逻辑,这边我们直接运行测试一下.

4.1、撰写任务一

task1就是我们任务一Bean的名称,handle就是任务二需要执行的具体方法。

package com.ithuang.quartz.task;

import org.springframework.stereotype.Component;

import java.util.Date;

@Component("task1")
public class Task1 {

    public void handle(){
        Date date = new Date();
        System.out.println("task1"+date);
    }

}

4.2、撰写任务二 

task2就是我们任务二Bean的名称,handle就是任务二需要执行的具体方法。

package com.ithuang.quartz.task;

import org.springframework.stereotype.Component;

import java.util.Date;

@Component("task2")
public class Task2 {

    public void handle(){
        Date date = new Date();
        System.out.println("task2"+date);
    }

}

4.3、启动

代码运行成功! 

五、Gitee源码地址

springboot整合quartz定时任务框架

六、总结 

这边就是我自己从0到1搭建了一个比较成熟的定时任务框架,参考的是目前主流的若依框架,我在他的框架上做了一些简化,不过也能应付大部分的场景了,非常的方便,功能也非常完善,整个项目的源码已经全部吃透,这边做一个总结,收获满满!

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

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

相关文章

Java并发(十二)----线程应用之多线程解决烧水泡茶问题

1、背景 统筹方法&#xff0c;是一种安排工作进程的数学方法。它的实用范围极广泛&#xff0c;在企业管理和基本建设中&#xff0c;以及关系复杂的科研项目的组织与管理中&#xff0c;都可以应用。 怎样应用呢&#xff1f;主要是把工序安排好。 比如&#xff0c;想泡壶茶喝。…

【前后端分离开发及项目部署流程】

文章目录 前后端分离开发技术1 前后端分离开发1.1 介绍1.2 开发流程1.3 前端技术栈&#xff08;了解&#xff09; 2 Yapi&#xff08;定义API接口&#xff09;2.1 介绍2.2 使用 3 Swagger3.1 介绍3.2 使用方式3.3 常用注解 4 项目部署4.1 部署架构4.2 部署环境说明4.3 部署前端…

chatgpt赋能python:如何使用Python访问共享目录——让共享变得简单易行

如何使用Python访问共享目录 —— 让共享变得简单易行 作为一种高效而强大的编程语言&#xff0c;Python拥有各种各样的应用。其中一个非常重要的应用场景就是对共享目录的访问和操作。无论是在家庭网络&#xff0c;企业内网或者云存储平台&#xff0c;共享目录的重要性毋庸置…

两个链表的入环节点(java)

两个链表的入环节点 两个链表的入环节点解题思路代码演示 链表相关的题 两个链表的入环节点 给定两个可能有环也可能无环的单链表&#xff0c;头节点head1和head2 请实现一个函数&#xff0c;如果两个链表相交&#xff0c;请返回相交的第一个节点。如果不相交返回null 要求如果…

ATTCK(一)之为什么要学习ATTCK

ATT&CK 简介 本系列旨在介绍网络红蓝对抗领域最好的ATT&CK矩阵模型&#xff0c;以期帮助有意愿深耕在红蓝对抗领域的人员能系统性的掌握红蓝对抗领域的知识和经验。本系列将详细ATT&CK的起源、发展历史&#xff0c;ATT&CK矩阵相对其他High-Level红蓝对抗模型…

Redis7【② Key通用命令 十大数据类型】

1 Key的通用命令 redis命令不区分大小写&#xff0c;但是key是区分大小写的。没有返回值的命令执行成功会返回1&#xff0c;失败返回0。 1. KEYS 查看所有的key&#xff0c;返回值是一个数组 2. EXISTS EXISTS key [key ...]&#xff1a;返回给定的key中已存在的个数&#xf…

前端Vue自定义验证码密码登录切换tabs选项卡标签栏标题栏 验证码登录模版 密码登录模版

前端Vue自定义验证码密码登录切换tabs选项卡标签栏标题栏 验证码登录模版 密码登录模版&#xff0c; 请访问uni-app插件市场地址&#xff1a;https://ext.dcloud.net.cn/plugin?id13221 效果图如下&#xff1a; 实现代码如下&#xff1a; # cc-selectBox #### 使用方法 使…

【计算机网络】可靠传输的实现机制

1、停止-等待协议SW 信道利用率 题目 小结 2.回退N帧协议GBN Go-Back-N 题目 小结

设计模式3:单例模式:JMM与volatile和synchronized的关系

本文目录 JMM简介Java 内部内存模型(The Internal Java Memory Model)硬件内存架构(Hardware Memory Architecture)弥合 Java 内存模型和硬件内存架构之间的差距(Bridging The Gap Between The Java Memory Model And The Hardware Memory Architecture)1.共享对象的可见性2.竞…

OpenStack(T版)——计算(Nova)服务介绍与安装

文章目录 OpenStack(T版)——计算(Nova)服务介绍与安装安装与配置(controller)准备(1)创建数据库(2)加载环境变量(3)创建认证服务凭据(4)创建Nova计算服务组件的API endpoint 安装和配置Nova计算服务组件(1)安装软件包(2)编辑/etc/nova/nova.conf 完成以下操作(3)同步数据库验证…

云服务器Linux防火墙云锁安装部署及使用 技术支持服务器安全运维

服务器必备安全防护及运维管理SAAS解决方案&#xff0c;支持windows/linux服务器跨平台实时、批量、远程安全管理&#xff0c;有效对抗服务器入侵和网络攻击。 服务器&#xff1a;Redhat/CentOS/Ubuntu/SUSE/中标麒麟 64位 Web中间件&#xff1a;Apache/Nginx/kangle/Tomcat/W…

【软考网络管理员】2023年软考网管初级常见知识考点(26)- HTML常见属性标签、表格、表单详解

涉及知识点 Html的概念&#xff0c;html常见标签&#xff0c;html常见属性&#xff0c;html表格&#xff0c;html表单&#xff0c;软考网络管理员常考知识点&#xff0c;软考网络管理员网络安全&#xff0c;网络管理员考点汇总。 原创于&#xff1a;CSDN博主-《拄杖盲学轻声码…

5-2图像处理经典案例:正弦噪声图像去噪

学习目标&#xff1a; 图像处理经典案例 去除噪声 1.简述 图像降噪的英文名称是Image Denoising&#xff0c; 图像处理中的专业术语。是指减少数字图像中噪声的过程&#xff0c;有时候又称为图像去噪。图像的噪声来源相对复杂&#xff0c;搞清楚图像噪声的成因对我们进行…

B+树

B树 B树是对B树的一种变形树&#xff0c;它与B树的差异在于: 非叶结点仅具有索引作用&#xff0c;也就是说&#xff0c;非叶子结点只存储key&#xff0c;不存储value 树的所有叶结点构成一个有序链表&#xff0c;可以按照key排序的次序遍历全部数据 B树存储数据 若参数M选…

使用影刀RPA拆分excel数据

首先&#xff0c;要使程序有一定的兼容性&#xff0c;即增加互动性&#xff0c;认为选择要拆分的文件和拆分的依据列&#xff0c;可以利用影刀中的‘打开选择对话框’和‘打开输入对话框’来实现&#xff0c;这样一来便不用考虑待拆分excel的路径问题获取1中选择的依据拆分列&a…

登录框界面之渗透测试思路总结

前言 大家都知道&#xff0c;渗透的过程中&#xff0c;遇见登录框是很常见的。下面就简单总结一下渗透中遇见登录页面的思路&#xff1a; 首先登录页面可能产生哪些漏洞呢&#xff1f; 1、弱密码与暴力破解 2、万能密码、SQL与XSS&#xff08;注入&#xff09; 3、登录时&…

渗透测试自动化报告脚本-----Nessus报告自动化解析--1-html解析

本专栏内容主要用于渗透测试工程师应对在工作中的自动化操作难题&#xff0c;高效摸鱼专用 解决问题 1、对Nessus导出的html报告进行自动化的提取操作&#xff0c;包括IP地址&#xff0c;漏洞个数&#xff0c;漏洞等级&#xff0c;漏洞描述&#xff0c;CVE编号等 2、由于Nes…

配置文件的优先级及maven打包和参数(port)的修改

1、配置文件的优先级 SpringBoot中支持五种配置格式:优先级&#xff1a;命令行参数(–xxxxxx) > java系统属性(-Dxxx xxx) > application.properties > application.yml > application.yaml 虽然springboot支持多种格式配置文件&#xff0c;但是在项目开发时&…

智能仓储货架的电子标签解决方案

近年来&#xff0c;电商和新零售行业的迅猛增长催生了仓储管理场景和运营模式的变革。企业不断寻求“低成本”和“更可靠”的解决方案&#xff0c;加快了仓储管理从粗放型向精细化转变的步伐。仓储管理的技术变革从机械化走向自动化&#xff0c;仓储数智化成为主流趋势。在这个…

chatgpt赋能python:Python语言冒泡排序-深入了解

Python语言冒泡排序 - 深入了解 冒泡排序是一种基本的排序算法&#xff0c;也是学习排序算法的入门算法之一。在Python中&#xff0c;我们可以很容易地实现冒泡排序。 冒泡排序的原理 冒泡排序的原理很简单&#xff0c;大概分为以下几个步骤&#xff1a; 比较相邻的元素&am…