Java 發送短信主流方案為調用第三方 HTTP API。本文提供基於 HttpClient 的生產級實現代碼,含簽名驗證、異常處理、配置管理。
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| HTTP API (WebService/REST) | 無硬體、接入快、支援集群 | 依賴網絡、需賬號餘額 | Web 應用、微服務、雲原生 |
| 短信貓 (串口/USB) | 自建、無外部依賴 | 硬體成本、維護複雜、併發低 | 內網隔離、極低量級 |
| SMPP 協議 | 高併發、運營商級 | 接入門檻高、需專線 | 大規模商業發送 |
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.nio.charset.StandardCharsets;
public class SmsSender {
private final String apiUrl;
private final String uid;
private final String key;
private final CloseableHttpClient httpClient;
public SmsSender(String apiUrl, String uid, String key) {
this.apiUrl = apiUrl;
this.uid = uid;
this.key = key;
this.httpClient = HttpClients.createDefault();
}
/**
* 發送短信
* @param mobile 手機號
* @param content 內容 (建議 ≤ 70 字符,超長自動分條計費)
* @return 平台返回碼/消息
*/
public String send(String mobile, String content) {
HttpPost post = new HttpPost(apiUrl);
post.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
String form = String.format("Uid=%s&Key=%s&smsMob=%s&smsText=%s",
uid, key, mobile, java.net.URLEncoder.encode(content, StandardCharsets.UTF_8));
post.setEntity(new StringEntity(form, ContentType.APPLICATION_FORM_URLENCODED));
try (CloseableHttpResponse resp = httpClient.execute(post)) {
String body = EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8);
int status = resp.getStatusLine().getStatusCode();
if (status != 200) throw new RuntimeException("HTTP " + status + ": " + body);
return body; // 平台具體返回格式查閱文檔
} catch (Exception e) {
throw new RuntimeException("發送失敗: " + mobile, e);
}
}
public void shutdown() throws Exception { httpClient.close(); }
}
# application.yml sms: api-url: https://sms.example.com/api/send uid: your_uid key: your_key sign-name: 【您的簽名】 # 內容前綴,按平台要求
@ConfigurationProperties(prefix = "sms")
@Component
public class SmsProperties {
private String apiUrl, uid, key, signName;
// getter/setter
}
@Service
public class SmsService {
private final SmsSender sender;
private final String signName;
public SmsService(SmsProperties props) {
this.sender = new SmsSender(props.getApiUrl(), props.getUid(), props.getKey());
this.signName = props.getSignName();
}
public void sendVerificationCode(String mobile, String code) {
String content = signName + "您的驗證碼是 " + code + ",5 分鐘內有效。";
sender.send(mobile, content);
}
@PreDestroy
public void destroy() throws Exception { sender.shutdown(); }
}
@Async + 線程池,避免阻塞業務線程// 示例,實際以平台文檔為準 0 成功 -1 參數錯誤 -2 賬號/密鑰錯誤 -3 餘額不足 -4 手機號格式錯誤 -5 內容含敏感詞 -6 同一手機發送過於頻繁