# SMS4J: one Java API for many SMS providers

> SMS4J is an Apache 2.0 licensed Java framework that puts more than twenty Chinese SMS providers behind a single interface, so that switching vendor is a change in a YAML file rather than a rewrite of the integration code.

**dromara/SMS4J** — 让简单的事情回归简单的本质。 SMS4J为短信聚合框架，帮您轻松集成多家短信服务，解决接入多个短信SDK的繁琐流程。 目前已接入数家常见的短信服务商，后续将会继续集成。后续的版本中我们还将推出更多的相关功能 你的 ⭐️ ⭐️⭐️Star⭐️⭐️ ⭐️，是我的动力！如果你觉得还不错，请点上一颗小星星

- Repository: https://github.com/dromara/SMS4J
- Website: https://sms4j.com
- Stars: 1,287 · Forks: 167
- Language: Java
- License: Apache-2.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/dromara-sms4j

## What SMS4J is for

SMS4J is a Java framework for sending SMS messages, published by the Dromara organisation and hosted at sms4j.com. The problem it addresses is described plainly in the README.

Third party SMS is the normal answer for a company that is not a bank or a telecom operator, and the third party SMS market in China has many vendors. Each vendor has its own request format, its own authentication style, its own template model and its own error codes. Every time a project needs SMS, someone reads another vendor's documentation and writes another small client. The stated goal of SMS4J is to unify those interfaces, so that even changing the SMS vendor only requires editing a YAML configuration file.

That is a narrower promise than a general messaging abstraction, and it is a useful one. The framework is aimed at the case where a team has already chosen a Chinese provider and expects to change its mind later, for price, for delivery rates or for a commercial reason. The abstraction is over providers, not over channels: there is no mention of email, voice or push in the framework itself, although the repository does contain email plugin modules.

The project is Apache 2.0 licensed, the current version in the README is 3.3.5, and the last push to the repository was on 2026-06-25. The project publishes no GitHub releases, and the branch description in the README says the master branch is the release branch whose versions go to the Maven Central repository.

## The provider list

The README lists the supported vendors, and the list is the clearest statement of the project's scope. It covers the major Chinese cloud platforms and a long tail of specialist SMS providers: Yimei Ruitong, Alibaba Cloud, Tencent Cloud, Huawei Cloud, JD Cloud, Ronglian Cloud, NetEase Yunxin, Tianyi Cloud, Qiniu Cloud, Heyi SMS, Yunpian, ZTong, Lianlu, Dingzhong, Chuanglan, Jiguang, Pudding Cloud, China Mobile Cloud MAS, Luosimao, SUBMAIL and Danmi Technology.

That is over twenty providers behind one interface, which is what makes the abstraction worth its overhead. The argument in the README is a time argument: a small outsourcing company, which the README names as the typical user, would otherwise spend its integration budget on reading documentation and writing adapter code instead of on the product.

The cloud providers and the specialist providers are not equivalent in what they offer. The Alibaba Cloud configuration in the README includes a signature, a template ID, a template variable name and a request URL that defaults to dysmsapi.aliyuncs.com, which is the model of a template based provider. The Huawei configuration adds an app key, a secret, a channel number, a callback URL and a region specific API URL, and its template ID is optional when a custom sending method is used. The ZTong entry is different again, where the access key ID is a terminal user name rather than a login account.

So the abstraction unifies the entry point, not the underlying model. A project that sends one kind of message through one provider will find that switching to a provider with a different template model needs more than a configuration change, and the README does not pretend otherwise, since each provider's own usage document has to be written when a provider is added.

## Spring Boot integration and the blends configuration

The Spring Boot path is the documented one. The dependency is a starter module:

```xml
<dependency>
 <groupId>org.dromara.sms4j</groupId>
 <artifactId>sms4j-spring-boot-starter</artifactId>
 <version>最新版本</version>
</dependency>
```

where the version placeholder is what the README shows. The configuration then lives in YAML, under an sms key, with a blends map whose keys are the project chosen identifiers for each provider. This is the part of the design that carries the promise: the identifier is yours, and the credentials behind it can point at a different vendor without touching the Java code.

```yaml
sms:
   config-type: yaml
   blends:
       自定义标识1:
         #阿里云的accessKey
         accessKeyId: 您的accessKey
         #阿里云的accessKeySecret
         accessKeySecret: 您的accessKeySecret
         #短信签名
         signature: 测试签名
         #模板ID 用于发送固定模板短信使用
         templateId: SMS_215125134
         #模板变量 上述模板的变量
         templateName: code
         #请求地址 默认为dysmsapi.aliyuncs.com 如无特殊改变可以不用设置
         requestUrl: dysmsapi.aliyuncs.com
```

The other entries in the same blends map show the provider specific keys, including a Huawei entry with appKey, app-secret, signature, sender, template-id, statusCallBack and url, and a ZTong entry whose accessKeyId and accessKeySecret are described as the end user name and password from the provider's own console rather than a developer account.

The config-type key selects the configuration source, and the presence of a yaml option implies other formats are possible. The README documents the Spring Boot path, and the repository also carries a Solon plugin and a plain Java SE plugin, so the framework is not tied to one application framework.

## Sending a message through SmsFactory

The sending API is a factory that resolves a configured blend by its identifier. The example in the README is a Spring controller that sends to two different providers in one method:

```java
@RestController
@RequestMapping("/test/")
public class DemoController {

    // 测试发送固定模板短信
    @RequestMapping("/")
    public void send() {
         //阿里云向此手机号发送短信
        SmsFactory.getSmsBlend("自定义标识1").sendMessage("18888888888","123456");
        //华为短信向此手机号发送短信
        SmsFactory.getSmsBlend("自定义标识2").sendMessage("16666666666","000000");
    }
}
```

Two things follow from that shape. The identifier in the code is the same string as the key in the YAML, which is the mechanism by which the vendor can change without a code change. And the parameters are a phone number and a value, which matches the template based providers in the configuration: the value is the template variable, and the template itself is defined on the provider's side and referenced by templateId in the configuration.

The return type of sendMessage is not shown in the example, so the error handling model is not documented in the README. That matters for a sending library, since a carrier rejection and a network timeout are different failures, and a team integrating it will want to know whether a failed send throws, returns a value or is recorded somewhere. The framework does not document that here, and the official documentation at sms4j.com and the JavaDoc published on Gitee are where to look.

## Asynchronous sending and the shared thread pool

Providers differ in how well they support asynchronous sending, and some do not support it at all. To give the framework one consistent asynchronous behaviour, SMS4J runs asynchronous sends on its own thread pool, configured under the sms key:

```yaml
sms:
  #核心线程池大小
  corePoolSize: 10
  #最大线程数
  maxPoolSize: 30
  #队列容量
  queueCapacity: 50
  #活跃时间
  keepAliveSeconds: 60
  # 线程名字前缀
  threadNamePrefix: sms-executor-
  #设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean
  shutdownStrategy: true
```

The defaults are ten core threads, thirty maximum threads and a queue capacity of fifty, with the thread names prefixed sms-executor- so the pool is identifiable in a thread dump. The README says these are the defaults and that they can be changed in the YAML file to suit the project.

Two of those settings have consequences worth naming. The queue capacity of fifty is small for a burst, and a burst of more than thirty concurrent sends plus fifty queued will start to reject rather than wait. The shutdownStrategy flag is the more interesting one: it makes the thread pool wait for all queued tasks to finish before it is destroyed along with the other beans, which prevents an application restart from silently dropping messages that were accepted but not yet sent. That is the correct default for a sending library and a common source of shutdown hangs in hand rolled implementations.

## Module layout, branches and how the project expects contributions

The repository is a Maven multi module project, and the module names describe the architecture. There is sms4j-api, the interface every provider implements, sms4j-comm, the shared communication layer, sms4j-core, the core implementation, and a sms4j-provider directory that holds the vendor adapters. Around them sit the integration modules: sms4j-spring-boot-starter, a Solon plugin with its own example project, a plain Java SE plugin, an email plugin and a Jakarta variant of it, and an OA plugin. There is also a Spring Boot example project.

The branch structure is described in the README. Master is the release branch, and the versions on it are what goes to Maven Central. The dev branch is where contributions are merged, and code moves from there to preview once it is settled. The contribution steps are the familiar fork, branch, commit, pull request sequence, with the pull request targeting the dev branch rather than master.

The contribution rules are specific and worth reading before sending a patch. Documentation for a new provider has to be written in Markdown alongside the code, comments are expected to follow the JavaDoc standard covering classes, methods, variables, parameters and return values, and new modules must not break the existing structure or compatibility. That last rule is what keeps the provider abstraction intact, and it explains why the provider list grows slowly and carefully rather than by a hundred thin adapters.

## Where SMS4J is the wrong tool

The first case is a service outside China. Every provider in the list is a Chinese company, and a business that sends SMS to international carriers is not served by any of them. The framework would have to be extended with a provider that does not exist yet, which the contribution rules make possible but nobody has done.

The second case is a project that will send exactly one message through one provider and never change it. The abstraction costs a dependency, a YAML layer and an indirection through a factory, and buys nothing that is not already provided by the vendor's own SDK. For a small internal tool, calling the provider directly is less code.

The third case is a project that needs to talk to the provider beyond plain sending. The visible API is one method that takes a number and a value, and the configuration model is built around signatures and templates. Delivery receipts, balance queries, number blacklists and multi-channel routing are not described in the README, and a business that needs a delivery report back into its own system may find that it has to write that part against the provider's API itself, which puts the vendor back into the code.

The real alternative is the direct one, and it is not a small thing to give up. Each major cloud provider ships its own well documented SDK with its own retry, signing and error handling, and a team that picks one and stays with it gets that provider's support and updates. What SMS4J offers instead is the freedom to leave, and for a business whose SMS costs and delivery rates are a live commercial concern, that freedom has a value that is easy to underestimate until the provider changes its terms.

## Conclusion

SMS4J fits a Java team that sends SMS through a Chinese provider and expects to change vendor later, since the provider list and the configuration driven blends are the whole point of the project. It does not fit a service outside China, where none of the listed providers operate. Check first that the provider you need is in the supported list, since the contribution rules require a new provider to ship with its own documentation.

## FAQ

### What is SMS4J and which SMS providers does it support?

SMS4J is an Apache 2.0 licensed Java framework that unifies more than twenty Chinese SMS providers behind one interface, including Alibaba Cloud, Tencent Cloud, Huawei Cloud, JD Cloud, NetEase Yunxin, Yunpian, SUBMAIL and China Mobile Cloud MAS. Switching vendor is meant to be a YAML change.

### How do I add SMS4J to a Spring Boot project?

Add the org.dromara.sms4j:sms4j-spring-boot-starter dependency, then configure the sms key in the YAML file with a blends map whose keys are your own identifiers and whose values hold the provider credentials, signature and template settings. The README links the Spring Boot integration steps and the official documentation at sms4j.com.

### How do I send a message with SMS4J?

Resolve the configured blend by its identifier and call sendMessage with a phone number and the template value, for example SmsFactory.getSmsBlend("自定义标识1").sendMessage("18888888888","123456"). The identifier matches the key in the blends configuration.

### How does SMS4J handle asynchronous sending?

Because providers differ in async support, SMS4J runs asynchronous sends on its own thread pool. The defaults are corePoolSize 10, maxPoolSize 30, queueCapacity 50, keepAliveSeconds 60 and a thread name prefix of sms-executor-, and the shutdownStrategy flag makes the pool wait for queued tasks before shutdown.

## Sources

- [dromara/SMS4J on GitHub](https://github.com/dromara/SMS4J)
- [Issues](https://github.com/dromara/SMS4J/issues)
- [License: Apache-2.0](https://github.com/dromara/SMS4J/blob/master/LICENSE)
- [Project website](https://sms4j.com)
- [README](https://github.com/dromara/SMS4J/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/dromara-sms4j
