Spring Boot定时发送短信

Spring Boot定时发送短信的两种方式

不少项目中估计都有遇到定时发送这个梗,我也遇到了,页面中下拉框选项有立即发送以及定时发送,立即发送就不用说了,定时发送可以讲一下,定时的方式有很多种,Java自带的定时器,Spring的定时器,大致讲一下我做的时候思路

1. Java自带的定时器

java.util包里的Timer,它也可以实现定时任务但是功能过于单一所有使用很少,而还有一个类ScheduledExecutorService可以基本满足定时任务中的需求

首先要创建一个线程,这里实现了Runnable接口,自己写run方法

public class MessageJob implements Runnable {

    private static final Logger LOGGER = LoggerFactory.getLogger(MessageJob.class);

    private static volatile ConcurrentHashMap<String, String> idMapping = new ConcurrentHashMap<>();

    private static volatile ConcurrentHashMap<String, MessageJob> cache = new ConcurrentHashMap<>();

    private volatile AtomicBoolean canceled = new AtomicBoolean(false);
    /** 任务id */
    private String jobId;
    /** 短信id */
    private String messageId;

    private UserDTO userDTO;

    private String title;

    private String context;

    private String phone;

    public MessageJob(String messageId, UserDTO UserDTO, String title, String context, String phone) {
        this.jobId = GeneratorIDFactory.generatorUUID();
        this.messageId = messageId;
        this.userDTO = UserDTO;
        this.title = title;
        this.context = context;
        this.phone = phone;
        //取消并清除上一次任务
        cancelAndClearLastJobIfExist();
        //缓存本次任务
        cacheThisJob();
    }

    /**
     * 取消并清除上一次任务
     */
    private void cancelAndClearLastJobIfExist() {
        if (StringUtil.isNotEmpty(idMapping.get(messageId))) {
            MessageJob lastJob = cache.get(idMapping.get(messageId));
            if (null != lastJob) {
                lastJob.cancelJob();
                cache.remove(idMapping.get(messageId));
                idMapping.remove(messageId);
            }
        }
    }

    @Override
    public void run() {
        //判断任务是否被取消
        if (!canceled.get()) {
            SendMessageUtil sendMessageUtil = (SendMessageUtil) SpringContextUtil.getBean("sendMessageUtil");
            MsgMessageService msgMessageService = (MsgMessageService) SpringContextUtil.getBean("msgMessageService");
            //发送
            try {
                sendMessageUtil.sendMessage(userDTO, title, context, phone);
                LOGGER.info("线程已启动");
                MsgMessage message = new MsgMessage();
                message.setState(1);
                message.setId(messageId);
                msgMessageService.update(message);
                LOGGER.info("短信状态已更新");
            } catch (ServiceException e) {
                LOGGER.error("线程出现问题::" + e.getMessage(), e);
            }
            //从缓存中清理本任务
            clear();
        }
    }

    /**
     * 缓存本次任务
     */
    private void cacheThisJob() {
        //id映射
        idMapping.put(this.messageId, this.jobId);
        //短信发送任务缓存
        cache.put(this.jobId, this);
    }

    /**
     * 取消任务
     */
    public void cancelJob() {
        this.canceled.set(true);
    }

    /**
     * 清理缓存
     */
    public void clear() {
        idMapping.remove(messageId);
        cache.remove(this.jobId);
    }

}

模拟了缓存,以及队列,jobId是UUID自动生成,防止ID重复,一个短信ID对应一个jobId,一个jobId对应一个线程的方式,确保线程分布执行,不干扰

这么做的意义在于,可在定时发送日期还没到时取消发送短信,一个短信发送时间经过编辑后可清空上一次任务新建一个线程,调用代码如下

ScheduledExecutorService service = new ScheduledThreadPoolExecutor(10, new BasicThreadFactory.Builder().namingPattern("scheduled-pool-%d").daemon(true).build());
MessageJob job = new MessageJob(msgMessage.getId(), sender, "短信通知", messagecontent, phones);
long delay = msgMessage.getSendertime().getTime() - System.currentTimeMillis();
service.schedule(job, delay, TimeUnit.MILLISECONDS);

delay是短信发送的时间减去当前时间的毫秒,让定时器在规定时间后执行这个线程发送短信

2. Spring的定时器注解

Spring Boot定时器就不介绍了,也很方便简单,在启动类上加上注解@EnableScheduling就可以了

这种方式比较适用于在项目中,短信的所有信息都存在数据里里的情况下,根据type来区分是定时发送还是立即发送,state来区别是已发送还是未发送;这个定时器只需要读取出所有状态是未发送并且是定时发送的数据库数据,并且发送时间是小于当前时间的

select * from message where type = 2 and state = 2 and send_time <= now();

举例,有一条短信是2019-1-17 22:05:00要发送的,定时任务一直在开启,到17号22点05分00秒的时候的时候这个now()就是当前时间,<=正好查到这条数据,那就表示到了该发送的时间,将短信发送出去

@Component
public class MessageJobTwo {

    private static final Logger logger = LoggerFactory.getLogger(MessageJobTwo.class);

    @Autowired
    private MsgMessageMapper messageMapper;

    @Autowired
    private MsgMessagemapperService msgMessagemapperService;

    @Autowired
    private SendMessageUtil sendMessageUtil;

    @Value("${message.isopen}")
    private boolean isOpen;

    /**
     * 每隔两秒定时查询小于等于当前时间的并且为未发送定时的数据
     * 对数据进行发送并且更改短信状态
     */
    @Scheduled(cron = "*/2 * * * * ?")
    public void sendMessageJob(){
        if (isOpen) {
            logger.info("开始执行-----短信定时任务");
            MsgMessageDTO msgMessageDTO = new MsgMessageDTO();
            msgMessageDTO.setType(2);
            msgMessageDTO.setState(2);
            msgMessageDTO.setSendertime(new Date());
            Map<String, Object> params = new HashMap<>(16);
            params.put("condition", msgMessageDTO);
            List<MsgMessageDTO> messageList = messageMapper.messageList(params);

            for (MsgMessageDTO messageDTO : messageList) {
                String phones = selectPhone(messageDTO.getId());
                sendMessageUtil.sendMessage(null, "短信通知", messageDTO.getMessagecontent(), phones);
                logger.info("短信发送成功---------");
                MsgMessage message = new MsgMessage();
                message.setState(1);
                message.setId(messageDTO.getId());
                messageMapper.update(message);
                logger.info("短信状态修改完成---------");
            }
        }
    }

    /**
     * 查询接收用户,拼接成字符串
     * @param messageId
     * @return
     */
    private String selectPhone(String messageId){
        Condition condition = new Condition(MsgMessagemapper.class);
        condition.createCriteria().andEqualTo("messageid", messageId);
        List<MsgMessagemapper> byCondition = msgMessagemapperService.findByCondition(condition);
        StringBuilder builder = new StringBuilder();
        for (MsgMessagemapper msgMessagemapper : byCondition) {
            builder.append(msgMessagemapper.getReceivephone()).append(",");
        }
        builder.deleteCharAt(builder.length() - 1);
        return builder.toString();
    }
}

结语

第二种方式比较方便,是不需要调用,项目启动之后会根据cron表达式里的时间进行操作,定时在查询,查出有符合要求的数据就进行发送。

第一种方式也可以,是异步多线程,第二种是单线程,看需求选择。

  • 4
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在Spring Boot发送短信,你可以使用第三方的短信服务提供商的API来实现。以下是一个简单的示例,展示了如何使用阿里云短信服务发送短信。 首先,你需要在阿里云注册一个账号,并获取到Access Key和Access Secret。然后,添加阿里云SDK的依赖到你的项目中。 在pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-core</artifactId> <version>4.5.3</version> </dependency> <dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-dysmsapi</artifactId> <version>1.1.0</version> </dependency> ``` 接下来,创建一个用于发送短信的工具类: ```java import com.aliyuncs.DefaultAcsClient; import com.aliyuncs.IAcsClient; import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest; import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse; import com.aliyuncs.exceptions.ClientException; import com.aliyuncs.profile.DefaultProfile; import com.aliyuncs.profile.IClientProfile; public class SmsUtil { private static final String ACCESS_KEY = "your-access-key"; private static final String ACCESS_SECRET = "your-access-secret"; private static final String SIGN_NAME = "your-sign-name"; private static final String TEMPLATE_CODE = "your-template-code"; public static void sendSms(String phoneNumber, String message) throws ClientException { // 设置超时时间-可自行调整 System.setProperty("sun.net.client.defaultConnectTimeout", "10000"); System.setProperty("sun.net.client.defaultReadTimeout",***
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值