springboot整合xxl-job,通过代码进行调度中心注册开启任务等

背景:由于工作需要,当用户在登录时自动触发定时任务。而不需要我们手动到调度中心管理页面去创建任务。
工程介绍:分为两个项目,第一个是调度中心的项目(xxl-job-admin)。第二个是我们自己的项目(myProject)。
步骤如下:

调度中心操作步骤

1.下载xxl-job的源码

点击进入xxl-job项目官网地址
源码下载地址-github
源码下载地址-gitee

1.1、gitee为例,下载2.4.0版本。

在这里插入图片描述

1.2、下载代码压缩包

在这里插入图片描述

1.3、下载完成,解压压缩包目录展示

在这里插入图片描述

1.4、数据库建表,打开doc文件夹,里面有sql文件。到数据库执行就可以创建xxl-job项目所需要的表了。

在这里插入图片描述

1.5、打开xxl-job-admin项目(我这里使用的是idea工具)

在这里插入图片描述
展开目录如下:
在这里插入图片描述

1.6、更新配置(主要两个配置:1.端口默认8080(也可以自定义)2.数据库配置))。报红地方不影响代码运行,可以不用管。

在这里插入图片描述

1.7、启动项目访问调度中心,访问地址:http://localhost:8080/xxl-job-admin,默认用户名:admin,默认密码:123456

a.启动日志:

在这里插入图片描述

b.登录页面:

在这里插入图片描述

c.登录进入首页

在这里插入图片描述
进入到以上页面就说明调度中心启动没有问题了,接下来就是改造调度中心为我们所用。

1.8、改造项目

1.8.0、任务组控制器添加新接口用于查找执行器(执行器的意思是,执行任务时用哪个执行器执行,必须要指定)。
a.进入JobGroupController,添加以下代码

在这里插入图片描述
代码如下:

	@RequestMapping("/loadByAppName")
    @ResponseBody
    @PermissionLimit(limit = false)
    public ReturnT<XxlJobGroup> removeJob(String param) {

        LoadByAppNameParam loadByAppNameParam = GsonTool.fromJson(param, LoadByAppNameParam.class);
        String appName = loadByAppNameParam.getAppName();
        XxlJobGroup jobGroup = xxlJobGroupDao.loadByAppName(appName);

        try {
            if (null != jobGroup) {
                return new ReturnT<XxlJobGroup>(jobGroup);
            } else {
                return new ReturnT<XxlJobGroup>(ReturnT.FAIL_CODE, null);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

注:@PermissionLimit(limit = false)注解的含义是跳过登录验证

b.进入XxlJobGroupDao文件添加如下代码

在这里插入图片描述
代码如下:

XxlJobGroup loadByAppName(@Param("name") String name);
c.进入XxlJobGroupMapper.xml文件添加如下代码

在这里插入图片描述
代码如下:

<select id="loadByAppName" parameterType="java.lang.String" resultMap="XxlJobGroup">
		SELECT
		<include refid="Base_Column_List"/>
		FROM xxl_job_group AS t
		WHERE t.app_name = #{name}
	</select>
d.LoadByAppNameParam.java文件是我自定义文件,用来接受appName参数的,后面会讲到。

代码如下:

public class LoadByAppNameParam {

    private String appName;

    public String getAppName() {
        return appName;
    }

    public void setAppName(String appName) {
        this.appName = appName;
    }

}
e.测试,我在调度管理中创建了一个默认执行器,用postman调用接口测试,查看返回数据。注意:后面我还是用xxl-job自带的执行器来演示

在这里插入图片描述
我设置的本地ip地址,不填的话默认是当前主机的ip
在这里插入图片描述
postman测试接口,查找名为“xxl-job-executor-sample”的执行器。
在这里插入图片描述

1.8.1、在JobInfoController.java中添加两个接口

在这里插入图片描述
代码如下:

@RequestMapping("/addJob")
    @ResponseBody
    @PermissionLimit(limit = false)
    public ReturnT<String> addJob(String param) {
        System.out.println(param);
        XxlJobInfo xxlJobInfo = GsonTool.fromJson(param, XxlJobInfo.class);
        return xxlJobService.add(xxlJobInfo);
    }

    @RequestMapping("/removeJob")
    @ResponseBody
    @PermissionLimit(limit = false)
    public ReturnT<String> removeJob(String id) {
        return xxlJobService.remove(Integer.parseInt(id));
    }

这两个接口后面在代码里测试这里就不再演示了。

我们自己项目的操作步骤

1.创建SpringBoot工程(SpringBoot版本2.5.6jdk版本为1.8xxl-job-core依赖为2.4.0lombok插件有使用到hutool工具包5.5.0阿里巴巴fastjson

pom文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.6</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.abliner</groupId>
    <artifactId>myProject</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>myProject</name>
    <description>myProject</description>
    <properties>
        <java.version>8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- xxl-job-core依赖 -->
        <!-- https://mvnrepository.com/artifact/com.xuxueli/xxl-job-core -->
        <dependency>
            <groupId>com.xuxueli</groupId>
            <artifactId>xxl-job-core</artifactId>
            <version>2.3.0</version>
        </dependency>
        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- hutool工具依赖 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-json</artifactId>
            <version>5.5.0</version>
        </dependency>
        <!-- JSON转换工具 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.11</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

1.0、配置文件application.yml(配置文件默认是application.properties,我喜欢另一种风格所以改成了application.yml。指定启动端口为:8081)

在这里插入图片描述
代码如下:

server:
  port: 8081

# Xxl-Job分布式定时任务调度中心
xxl:
  job:
    ### 执行器通讯TOKEN [选填]:非空时启用;
    accessToken: default_token
    admin:
      ### 调度中心部署根地址 [选填]:如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册""任务结果回调";为空则关闭自动注册;
      addresses: http://127.0.0.1:8080/xxl-job-admin
    executor:
      ### 执行器注册 [选填]:优先使用该配置作为注册地址,为空时使用内嵌服务 ”IP:PORT“ 作为注册地址。从而更灵活的支持容器类型执行器动态IP和动态映射端口问题。
      address:
      ### 执行器AppName [选填]:执行器心跳注册分组依据;为空则关闭自动注册
      appname: xxl-job-executor-sample
      ### 执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册""调度中心请求并触发任务";
      ip:
      ### 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口;
      port: 9999
      ### 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径;
      logpath: /data/applogs/xxl-job/jobhandler
      ### 执行器日志文件保存天数 [选填] : 过期日志自动清理, 限制值大于等于3时生效; 否则,-1, 关闭自动清理功能;
      logretentiondays: 30

注意:配置调度中心的地址addresses,我这里用的默认的地址:http://127.0.0.1:8080/xxl-job-admin,如调度中心端口更改,那么我们自己项目里的端口也需要更新。accessToken也是一样,要与调度中心配置保持一致。

1.1、创建配置

1.1.0、创建xxl的配置(没有目录的新建目录,我这里新建了configure和xxl两个目录)

注意:XxlJobConfig.java中忘记加“@Configuration注解”,导致调度中心注册失败,日志提示如下图:
在这里插入图片描述

代码演示:
在这里插入图片描述

完整代码如下:

package com.abliner.myproject.configure.xxl;

import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;

/**
 * @author
 * @date 2023/12/11 14:58
 * @describe
 */
 @Configuration
public class XxlJobConfig {
    private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);

    @Value("${xxl.job.admin.addresses}")
    private String adminAddresses;

    @Value("${xxl.job.accessToken}")
    private String accessToken;

    @Value("${xxl.job.executor.appname}")
    private String appname;

    @Value("${xxl.job.executor.address}")
    private String address;

    @Value("${xxl.job.executor.ip}")
    private String ip;

    @Value("${xxl.job.executor.port}")
    private int port;

    @Value("${xxl.job.executor.logpath}")
    private String logPath;

    @Value("${xxl.job.executor.logretentiondays}")
    private int logRetentionDays;


    @Bean
    public XxlJobSpringExecutor xxlJobExecutor() {
        logger.info(">>>>>>>>>>> xxl-job config init.");
        XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
        xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
        xxlJobSpringExecutor.setAppname(appname);
        xxlJobSpringExecutor.setAddress(address);
        xxlJobSpringExecutor.setIp(ip);
        xxlJobSpringExecutor.setPort(port);
        xxlJobSpringExecutor.setAccessToken(accessToken);
        xxlJobSpringExecutor.setLogPath(logPath);
        xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);

        return xxlJobSpringExecutor;
    }

    /**
     * 针对多网卡、容器内部署等情况,可借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP;
     *
     *      1、引入依赖:
     *          <dependency>
     *             <groupId>org.springframework.cloud</groupId>
     *             <artifactId>spring-cloud-commons</artifactId>
     *             <version>${version}</version>
     *         </dependency>
     *
     *      2、配置文件,或者容器启动变量
     *          spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.'
     *
     *      3、获取IP
     *          String ip_ = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
     */


}

1.1.1、创建RestTemplateConfig.java用于网络请求的配置

在这里插入图片描述
完整代码如下:

package com.abliner.myproject.configure;

import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.List;

/**
 * @author
 * @date 2023/12/11 16:19
 * @describe
 */
@Configuration
public class RestTemplateConfig {
    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        //先获取到converter列表
        List<HttpMessageConverter<?>> converters = builder.build().getMessageConverters();
        for (HttpMessageConverter<?> converter : converters) {
            //因为我们只想要jsonConverter支持对text/html的解析
            if (converter instanceof MappingJackson2HttpMessageConverter) {
                try {
                    //先将原先支持的MediaType列表拷出
                    List<MediaType> mediaTypeList = new ArrayList<>(
                            converter.getSupportedMediaTypes());
                    //加入对text/html的支持
                    mediaTypeList.add(MediaType.TEXT_PLAIN);
                    //将已经加入了text/html的MediaType支持列表设置为其支持的媒体类型列表
                    ((MappingJackson2HttpMessageConverter) converter)
                            .setSupportedMediaTypes(mediaTypeList);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return builder.build();
    }
}

1.2、创建XxlJobInfo.java(没有目录的新建目录,我这里新建了model目录。这个对象后面会使用到)

在这里插入图片描述
完整代码如下:

package com.abliner.myproject.model;

import java.util.Date;

/**
 * @author
 * @date 2023/12/11 15:14
 * @describe
 */
public class XxlJobInfo {

    private int id;				// 主键ID

    private int jobGroup;		// 执行器主键ID
    private String jobDesc;

    private Date addTime;
    private Date updateTime;

    private String author;		// 负责人
    private String alarmEmail;	// 报警邮件

    private String scheduleType;			// 调度类型
    private String scheduleConf;			// 调度配置,值含义取决于调度类型
    private String misfireStrategy;			// 调度过期策略

    private String executorRouteStrategy;	// 执行器路由策略
    private String executorHandler;		    // 执行器,任务Handler名称
    private String executorParam;		    // 执行器,任务参数
    private String executorBlockStrategy;	// 阻塞处理策略
    private int executorTimeout;     		// 任务执行超时时间,单位秒
    private int executorFailRetryCount;		// 失败重试次数

    private String glueType;		// GLUE类型	#com.xxl.job.core.glue.GlueTypeEnum
    private String glueSource;		// GLUE源代码
    private String glueRemark;		// GLUE备注
    private Date glueUpdatetime;	// GLUE更新时间

    private String childJobId;		// 子任务ID,多个逗号分隔

    private int triggerStatus;		// 调度状态:0-停止,1-运行
    private long triggerLastTime;	// 上次调度时间
    private long triggerNextTime;	// 下次调度时间


    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getJobGroup() {
        return jobGroup;
    }

    public void setJobGroup(int jobGroup) {
        this.jobGroup = jobGroup;
    }

    public String getJobDesc() {
        return jobDesc;
    }

    public void setJobDesc(String jobDesc) {
        this.jobDesc = jobDesc;
    }

    public Date getAddTime() {
        return addTime;
    }

    public void setAddTime(Date addTime) {
        this.addTime = addTime;
    }

    public Date getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(Date updateTime) {
        this.updateTime = updateTime;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getAlarmEmail() {
        return alarmEmail;
    }

    public void setAlarmEmail(String alarmEmail) {
        this.alarmEmail = alarmEmail;
    }

    public String getScheduleType() {
        return scheduleType;
    }

    public void setScheduleType(String scheduleType) {
        this.scheduleType = scheduleType;
    }

    public String getScheduleConf() {
        return scheduleConf;
    }

    public void setScheduleConf(String scheduleConf) {
        this.scheduleConf = scheduleConf;
    }

    public String getMisfireStrategy() {
        return misfireStrategy;
    }

    public void setMisfireStrategy(String misfireStrategy) {
        this.misfireStrategy = misfireStrategy;
    }

    public String getExecutorRouteStrategy() {
        return executorRouteStrategy;
    }

    public void setExecutorRouteStrategy(String executorRouteStrategy) {
        this.executorRouteStrategy = executorRouteStrategy;
    }

    public String getExecutorHandler() {
        return executorHandler;
    }

    public void setExecutorHandler(String executorHandler) {
        this.executorHandler = executorHandler;
    }

    public String getExecutorParam() {
        return executorParam;
    }

    public void setExecutorParam(String executorParam) {
        this.executorParam = executorParam;
    }

    public String getExecutorBlockStrategy() {
        return executorBlockStrategy;
    }

    public void setExecutorBlockStrategy(String executorBlockStrategy) {
        this.executorBlockStrategy = executorBlockStrategy;
    }

    public int getExecutorTimeout() {
        return executorTimeout;
    }

    public void setExecutorTimeout(int executorTimeout) {
        this.executorTimeout = executorTimeout;
    }

    public int getExecutorFailRetryCount() {
        return executorFailRetryCount;
    }

    public void setExecutorFailRetryCount(int executorFailRetryCount) {
        this.executorFailRetryCount = executorFailRetryCount;
    }

    public String getGlueType() {
        return glueType;
    }

    public void setGlueType(String glueType) {
        this.glueType = glueType;
    }

    public String getGlueSource() {
        return glueSource;
    }

    public void setGlueSource(String glueSource) {
        this.glueSource = glueSource;
    }

    public String getGlueRemark() {
        return glueRemark;
    }

    public void setGlueRemark(String glueRemark) {
        this.glueRemark = glueRemark;
    }

    public Date getGlueUpdatetime() {
        return glueUpdatetime;
    }

    public void setGlueUpdatetime(Date glueUpdatetime) {
        this.glueUpdatetime = glueUpdatetime;
    }

    public String getChildJobId() {
        return childJobId;
    }

    public void setChildJobId(String childJobId) {
        this.childJobId = childJobId;
    }

    public int getTriggerStatus() {
        return triggerStatus;
    }

    public void setTriggerStatus(int triggerStatus) {
        this.triggerStatus = triggerStatus;
    }

    public long getTriggerLastTime() {
        return triggerLastTime;
    }

    public void setTriggerLastTime(long triggerLastTime) {
        this.triggerLastTime = triggerLastTime;
    }

    public long getTriggerNextTime() {
        return triggerNextTime;
    }

    public void setTriggerNextTime(long triggerNextTime) {
        this.triggerNextTime = triggerNextTime;
    }
}

1.3、添加XxlUtil.java工具类,我们要远程调用调度中心需要用到(我这里新建了tool目录)

在这里插入图片描述
完整代码如下:

package com.abliner.myproject.tool;

import cn.hutool.json.JSONUtil;
import com.abliner.myproject.model.XxlJobInfo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.Map;

/**
 * @author
 * @date 2023/12/11 15:22
 * @describe
 */
@Component
@RequiredArgsConstructor
public class XxlUtil {

    @Value("${xxl.job.admin.addresses}")
    private String xxlJobAdminAddress;

    private final RestTemplate restTemplate;

    // xxl-job各种请求地址
    private static final String ADD_INFO_URL = "/jobinfo/addJob";
    private static final String REMOVE_INFO_URL = "/jobinfo/removeJob";
    private static final String GET_GROUP_ID = "/jobgroup/loadByAppName";

    /**
     * 添加任务
     *
     * @param xxlJobInfo
     * @param appName
     * @return
     */
    public String addJob(XxlJobInfo xxlJobInfo, String appName) {
        //组装参数
        Map<String, Object> params = new HashMap<>();
        params.put("appName", appName);
        String json = JSONUtil.toJsonStr(params);
        //调用xxl-job接口添加任务
        String result = doPost(xxlJobAdminAddress + GET_GROUP_ID, json);

        //获取执行器的id
        JSONObject jsonObject = JSON.parseObject(result);
        Map<String, Object> map = (Map<String, Object>) jsonObject.get("content");
        Integer groupId = (Integer) map.get("id");


        xxlJobInfo.setJobGroup(groupId);
        String xxlJobInfoJson = JSONUtil.toJsonStr(xxlJobInfo);
        //添加这个job
        return doPost(xxlJobAdminAddress + ADD_INFO_URL, xxlJobInfoJson);
    }

    // 删除job
    public String removeJob(long jobId) {
        MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
        map.add("id", String.valueOf(jobId));
        return doPostWithFormData(xxlJobAdminAddress + REMOVE_INFO_URL, map);
    }

    /**
     * 远程调用
     *
     * @param url
     * @param json
     */
    private String doPost(String url, String json) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
        map.add("param", json);
        HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(map, headers);
        ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, entity, String.class);
        return responseEntity.getBody();
    }

    private String doPostWithFormData(String url, MultiValueMap<String, String> map) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(map, headers);
        ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, entity, String.class);
        return responseEntity.getBody();
    }

}

1.4、编写XxlService.java(注意新建service和impl目录)

在这里插入图片描述
完整代码如下:

package com.abliner.myproject.service.impl;

import com.abliner.myproject.model.XxlJobInfo;
import com.abliner.myproject.tool.XxlUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

/**
 * @author
 * @date 2023/12/11 15:43
 * @describe
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class XxlService {

    private final XxlUtil xxlUtil;

    @Value("${xxl.job.executor.appname}")
    private String appName;

    public void addJob(XxlJobInfo xxlJobInfo) {
        xxlUtil.addJob(xxlJobInfo, appName);
        log.info("任务已添加");
    }

}

1.5、新建时间转换工具类DateUtils.java

在这里插入图片描述
完整代码如下:

package com.abliner.myproject.tool;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * @author
 * @date 2023/12/11 15:56
 * @describe
 */
public class DateUtils {

    // 使用 DateTimeFormatter 格式化时间
    public static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy MM dd HH mm ss");

    /**
     * 时间转换Cron表达式
     *
     * @param dateTime
     * @return Cron表达式
     * @author
     */
    public static String getCron(LocalDateTime dateTime) {

        String formattedDateTime = dateTime.format(formatter);

        String[] dateTimeParts = formattedDateTime.split(" ");
        String cron = String.format("%s %s %s %s %s ? %s-%s", dateTimeParts[5], dateTimeParts[4], dateTimeParts[3], dateTimeParts[2], dateTimeParts[1], dateTimeParts[0], dateTimeParts[0]);

        return cron;
    }

}

1.6、模拟登录请求

1.6.0、controlelr层->LoginController.java

在这里插入图片描述
完整代码如下:

package com.abliner.myproject.controller;

import com.abliner.myproject.service.LoginService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * @author
 * @date 2023/12/11 15:40
 * @describe
 */
@RestController
@RequestMapping("server")
public class LoginController {

    @Resource
    private LoginService loginService;

    @GetMapping("/login")
    public String login(@RequestParam("name") String name,
                        @RequestParam("password") String password) {
        loginService.login(name, password);
        return "登录成功";
    }

}

1.6.2、service层->LoginService.java

在这里插入图片描述
完整代码如下:

package com.abliner.myproject.service;

/**
 * @author
 * @date 2023/12/11 15:48
 * @describe
 */
public interface LoginService {

    /**
     * 登录
     *
     * @param name
     * @param password
     * */
    void login(String name,String password);

}

1.6.3、service实现层->LoginServiceImpl.java

在这里插入图片描述

完整代码如下:

package com.abliner.myproject.service.impl;

import com.abliner.myproject.model.XxlJobInfo;
import com.abliner.myproject.service.LoginService;
import com.abliner.myproject.tool.DateUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;

/**
 * @author 
 * @date 2023/12/11 15:51
 * @describe
 */
@Service
public class LoginServiceImpl implements LoginService {

    @Resource
    private XxlService xxlService;

    /**
     * 登录
     *
     * @param name
     * @param password
     */
    @Override
    public void login(String name, String password) {
        if (!StringUtils.isEmpty(password)) {
            if ("123456".equals(password)) {
                // 登录成功
                // 创建一个1分钟后向用户问好的任务
                LocalDateTime scheduleTime = LocalDateTime.now().plusMinutes(1L);

                XxlJobInfo xxlJobInfo = new XxlJobInfo();
                xxlJobInfo.setJobDesc("定时给用户发送通知");
                xxlJobInfo.setAuthor("imHJ");
                xxlJobInfo.setScheduleType("CRON");
                xxlJobInfo.setScheduleConf(DateUtils.getCron(scheduleTime));
                xxlJobInfo.setGlueType("BEAN");
                xxlJobInfo.setExecutorHandler("sayHelloHandler");
                xxlJobInfo.setExecutorParam(name);
                xxlJobInfo.setMisfireStrategy("DO_NOTHING");
                xxlJobInfo.setExecutorRouteStrategy("FIRST");
                xxlJobInfo.setExecutorBlockStrategy("SERIAL_EXECUTION");
                xxlJobInfo.setTriggerStatus(1);
                //将任务提交到xxl-job-admin
                xxlService.addJob(xxlJobInfo);
            }
        }
    }
}

1.6.4、新建JobHandler.java执行器,用于处理任务(定时任务执行的具体业务)

在这里插入图片描述
完整代码如下:

package com.abliner.myproject.handler;

import com.abliner.myproject.tool.XxlUtil;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * @author
 * @date 2023/12/11 16:05
 * @describe
 */
@Component
@Slf4j
public class JobHandler {

    private final XxlUtil xxlUtil;

    public JobHandler(XxlUtil xxlUtil) {
        this.xxlUtil = xxlUtil;
    }

    @XxlJob(value = "sayHelloHandler")
    public void execute() {
        String userName = XxlJobHelper.getJobParam();
        log.info("欢迎您: {}!", userName);
        // 避免一次性任务,留在界面,这里手动删除处理掉
        long jobId = XxlJobHelper.getJobId();
        xxlUtil.removeJob(jobId);
    }

}

以上是全部代码的编写,最后测试,还是用postman来测试。
A.postman展示
在这里插入图片描述

B.调度中心展示
在这里插入图片描述
C.idea控制台展示
在这里插入图片描述
最后整个流程执行完成。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于ssm实验室开放管理系统论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本实验室开放管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信…

为什么推荐将静态资源放到CDN上?

一、什么是静态资源 说到静态资源&#xff0c;我们还要知道什么是动态资源 静态资源 静态资源是指在服务器上存储的不经常改变的文件&#xff0c;如图片、CSS 文件、JavaScript 文件、字体文件等。与之相对的是动态资源&#xff0c;动态资源是根据用户请求和服务器端处理生成的…

【后端学前端】第二天 css动画 动感菜单(css变量、过渡动画、过渡延迟、js动态切换菜单)

目录 1、学习信息 2、源码 3、变量 1.1 定义变量 1.2 使用变量 1.3 calc() 函数 4、定位absolute和fixed 5、transform 和 transition&#xff0c;动画 5.1 变形transform 5.2 transition 5.3 动画animation 6、todo 1、学习信息 视频地址&#xff1a;css动画 动感菜…

.9.png的创建

1、创建.9.png 选中图片&#xff0c;右击&#xff0c;选择Create 9-Patch file&#xff0c;点击确定会生成一个xxx.9.png的图片 2、绘制拉伸区域 在图片的最外边界绘制拉伸区域&#xff0c;按住鼠标左键不放&#xff0c;绘制完成后保存就可以使用了。绘制结果示意如下&…

【AI绘画】万字长文——(超详细)ControlNet的详细介绍使用Stable Diffusion的艺术二维码完全生成攻略

目录 前言一、名词解释1-1、Stable Diffusion介绍1-2、ControlNet介绍1-2-1、ControlNet介绍&工作原理1-2-2、ControlNet控制方法介绍 1-3、案例分析1-3-1、室内装修设计1-3-2、品牌创意海报 1-4、stable-diffusion-webui 的参数解释 二、生成方法2-1、图像到图像2-1-1、二…

nodejs微信小程序+python+PHP健身服务应用APP-计算机毕业设计推荐 android

人类的进步带动信息化的发展&#xff0c;使人们生活节奏越来越快&#xff0c;所以人们越来越重视信息的时效性。以往的管理方式已经满足不了人们对获得信息的方式、方便快捷的需求。即健身服务应用APP慢慢的被人们关注。首先&#xff0c;网上获取信息十分的实时、便捷&#xff…

工业级路由器在货运物流仓储管理中的应用

工业级路由器在货运物流仓储管理中扮演着重要的角色&#xff0c;为整个物流系统提供了稳定可靠的网络连接和数据传输支持。下面将从以下几个方面介绍工业级路由器在货运物流仓储管理中的应用。 实时监控和追踪&#xff1a;工业级路由器通过与各种传感器、监控设备和物联网设备的…

高仿互站网站源码 后台手机端两套模板 电脑端二十套模版

源码简介 高仿互站网 后台手机端两套模板 电脑端二十套模版&#xff0c;简单介绍几个功能&#xff0c; 支持用户注册开店 开店申请&#xff0c;支持用户发布自己商品 支持卡密形式或实物形式&#xff0c; 支持用户自己发布求助 任务大厅功能&#xff0c;源码完整

解决火狐浏览器拖拽事件打开新页面的问题

产生原因及解决方案 我们在进行拖拽事件的编写时会发现&#xff0c;在火狐浏览器上会发生打开新窗口的问题&#xff0c;这是火狐浏览器的一个特性。 这是因为在 Firefox 中 ondrop 事件会触发 Firefox 自带的拖拽搜索功能&#xff0c;在 ondrop 事件触发执行时触发的函数中加…

A-23 P离子交换树脂:高效去除无机有机污染物的新选择

在当今水处理行业中&#xff0c;高效、环保的离子交换树脂备受关注。本文将为您介绍一款具有卓越性能的碱性季胺基阴离子交换树脂——Tulsion A-23 P。通过分析其特性和应用&#xff0c;展示其在水处理领域的优势。 一、Tulsion A-23 P离子交换树脂的特性 物理化学稳定性&#…

<习题集><LeetCode><队列><225/232/387/622/641>

目录 225. 用队列实现栈 232. 用栈实现队列 387. 字符串中的第一个唯一字符 622. 设计循环队列 641. 设计循环双端队列 225. 用队列实现栈 https://leetcode.cn/problems/implement-stack-using-queues/ class MyStack{private Queue<Integer> queue1;private Queu…

SQL命令---修改字段的数据类型

介绍 使用sql语句修改字段的数据类型。 命令 alter table 表明 modify 字段名 数据类型;例子 有一张a表&#xff0c;表里有一个id字段&#xff0c;长度为11。使用命令将长度修改为12 下面使用命令进行修改&#xff1a; alter table a modify id int(12) NOT NULL;下面使修…

基于lambda简化设计模式

前言 虽说使用设计模式可以让复杂的业务代码变得清晰且易于维护&#xff0c;但是某些情况下&#xff0c;开发可能会遇到我为了简单的业务逻辑去适配设计模式的情况&#xff0c;本文笔者就以四种常见的设计模式为例&#xff0c;演示如何基于lambda来简化设计模式的实现。 策略…

node.js express JWT token生成与校验

目录 JWT header&#xff08;标头&#xff09; payload&#xff08;有效负载&#xff09; signature&#xff08;签名&#xff09; 访问令牌&#xff08;token&#xff09; express jwt生成、验证 生成jwt 验证jwt JWT JWT 是轻量级的数据交换格式&#xff0c;相对于传…

银行数据分析入门篇:信用卡全生命周期分析,到底应该怎么做?

最近有朋友向我咨询银行信贷业务的数据分析&#xff0c;就看了很多案例&#xff0c;刚好看到一个信用卡全生命周期分析的案例&#xff0c;做得很详细又通俗易懂&#xff0c;基本上可以直接复制套用&#xff0c;所以特地分享给大家。 本文主要分享作者整个分析作品的思路&#x…

力扣17. 电话号码的字母组合(java 回溯法)

Problem: 17. 电话号码的字母组合 文章目录 题目描述思路解题方法复杂度Code 题目描述 思路 题目给定一串数字&#xff0c;要求我们找出所有可能的字母组合&#xff0c;即我们可以穷举出所有可能的结果&#xff0c;而涉及到穷举我们自然可以想到利用回溯来解决问题&#xff0c…

我的NPI项目之Android 安全系列 -- Android Strongbox 初识

从Android9(Pie)开始,Google强烈建议支持Strongbox. 具体描述如下: 一直到目前的Android14. 对应的内容也一并贴出来: 说人话就是Android开始通过独立于主SoC的单元进行密钥存储了。 通常&#xff0c;这样的单元就是我们通常称作的Secure Element&#xff08;SE&#xff09;&am…

5分钟搞懂K8S Pod Terminating/Unknown故障排查

Kubernetes集群中的Pod有时候会进入Terminating或Unknown状态&#xff0c;本文列举了6种可能的原因&#xff0c;帮助我们排查这种现象。原文: K8s Troubleshooting — Pod in Terminating or Unknown Status 有时我们会看到K8S集群中的pod进入"Terminating"或"U…

FFmpeg-基础组件-AVFrame

本章主要介绍FFmpeg基础组件AVFrame. 文章目录 1.结构体成员2.成员函数AVFrame Host内存的获取 av_frame_get_bufferAVFrame device内存获取av_hwframe_get_buffer&#xff08;&#xff09; 1.结构体成员 我们把所有的代码先粘贴上来&#xff0c;在后边一个一个解释。 typede…