阿里云短信服务还是非常好的,接口稳定,同时还提供SDK
,使得企业的接入该服务更加方便。下面来看看阿里云提供的发短信的java实例代码吧:
import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
/*
pom.xml
com.aliyun
aliyun-java-sdk-core
4.0.3
*/
public class SendSms {
public static void main(String[] args) {
DefaultProfile profile = DefaultProfile.getProfile("default", "", "");
IAcsClient client = new DefaultAcsClient(profile);
CommonRequest request = new CommonRequest();
request.setMethod(MethodType.POST);
request.setDomain("dysmsapi.aliyuncs.com");
request.setVersion("2017-05-25");
request.setAction("SendSms");
request.putQueryParameter("RegionId", "default");
try {
CommonResponse response = client.getCommonResponse(request);
System.out.println(response.getData());
} catch (ServerException e) {
e.printStackTrace();
} catch (ClientException e) {
e.printStackTrace();
}
}
}
代码是非常简洁的,补充一些必要的信息就可以发送短信了。接下来对代码执行流程进行分析:
第一步调用了一个静态方法,返回一个DefaultProfile
类型的实例
DefaultProfile.getProfile("default", "
进入这个方法里面:
public synchronized static DefaultProfile getProfile(String regionId, String accessKeyId, String secret) {
Credential creden = new Credential(accessKeyId, secret);
profile = new DefaultProfile(regionId, creden);
return profile;
}
这个方法用了synchronized
关键字修饰,它是一个同步方法,但是并不能保证在多线程环境下只能创建唯一该类实例,就算是在单线程下也不能。不知道为什么要这样(暂时不管啦)
在这个方法里首先实例化一个Credential
,这是一个凭据类,里面包含的属性如下:
# 这个属性记录凭据刷新的时间,当实例化时,就会给它赋值
# this.refreshDate = new Date();
private final Date refreshDate;
# 过期时间,如果时间超过这个时间就算是过期啦
private Date expiredDate;
# 申请阿里云短信时,所获得的
private String accessKeyId;
private String accessSecret;
# 一个安全的Token,暂时还用不到
private String securityToken;
到这里,我们可以知道,AccessKeyID访问者身份,AccessKeySecret加密签名字符串和服务器端验证签名字符串的密钥都是保存在这个凭据对象中的。
然后向下一行代码
profile = new DefaultProfile(regionId, creden);
这里把凭据对象creden
传入了DefaultProfile
构造器中
private DefaultProfile(String region, Credential creden) {
this.iendpoints = new LocalEndpointResolver();
this.remoteProvider = LocationServiceEndpointResolver.initRemoteEndpointsParser();
# 这个构造器主要是将这两个对象进行保存
this.credential = creden;
this.regionId = region;
this.locationConfig = new LocationConfig();
}
最后实例化的DefaultProfile
保存在该类中的静态属性中了
private static DefaultProfile profile = null;
总结:到这里就是将能辨识我们身份的信息进行储存
接下啦就是执行下面的代码啦:
IAcsClient client = new DefaultAcsClient(profile);
public DefaultAcsClient(IClientProfile profile) {
this.clientProfile = profile;
this.credentialsProvider = new StaticCredentialsProvider(profile);
this.clientProfile.setCredentialsProvider(this.credentialsProvider);
initSslSocketFactory();
}
private void initSslSocketFactory(){
try {
this.sslSocketFactory = HttpsUtils.buildJavaSSLSocketFactory(clientProfile.getCertPath());
}catch(SSLException e){
// keep exceptions for keep compatible
System.err.println("buildSSLSocketFactory failed" + e.toString());
}
}
从上面的流程上,profile
储存到了DefaultAcsClient
中的clientProfile
属性中了。同时又传入到了StaticCredentialsProvider
的构造器中。
public StaticCredentialsProvider(IClientProfile clientProfile) {
this.clientProfile = clientProfile;
Credential legacyCredential = this.clientProfile.getCredential();
if (null != legacyCredential.getSecurityToken()) {
this.credentials = new BasicSessionCredentials(
legacyCredential.getAccessKeyId(),
legacyCredential.getAccessSecret(),
legacyCredential.getSecurityToken()
);
} else {
this.credentials = new LegacyCredentials(legacyCredential);
}
}
到这里我们就可以知道凭据对象中的securityToken
属性的在什么时候起作用的了。
StaticCredentialsProvider
类不仅存储了profile
对象,同时还使用凭据对象中的SecurityToken
做判断。
实例化BasicSessionCredentials
赋值给自己的credentials
属性
该构造器执行完了之后得到的实例对象赋值给DefaultAcsClient
的credentialsProvider
属性
现在开始执行这一步:this.clientProfile.setCredentialsProvider(this.credentialsProvider);
这是profile
对象中的一个方法,进入:
@Override
public void setCredentialsProvider(AlibabaCloudCredentialsProvider credentialsProvider) {
if (credential != null) {
return;
}
credential = new CredentialsBackupCompatibilityAdaptor(credentialsProvider);
}
分析可以知道这个方法会直接返回,因为credential != null
为true
后面会执行这个方法:initSslSocketFactory();
private void initSslSocketFactory(){
try {
# 从名称上看,就可以知道这个方法的作用主要是初始化socket连接的工厂
this.sslSocketFactory = HttpsUtils.buildJavaSSLSocketFactory(clientProfile.getCertPath());
}catch(SSLException e){
// keep exceptions for keep compatible
System.err.println("buildSSLSocketFactory failed" + e.toString());
}
}
clientProfile.getCertPath()
:这行代码就是返回certPath
,他没有被设置,所有返回为null
,它的结果传入到静态方法buildJavaSSLSocketFactory
中,
public static SSLSocketFactory buildJavaSSLSocketFactory(String certPath) throws SSLException {
String trustCertPath = certPath != null ? certPath : getTrustCertPath();
String truststoreFile = getTruststoreFile();
if (trustCertPath == null && truststoreFile == null) {
return null;
} else {
try {
String secureSocketProtocol = getSecureSocketProtocol();
SSLContext sc = SSLContext.getInstance(secureSocketProtocol);
TrustManager[] trustManagers = null;
if (trustCertPath != null) {
trustManagers = buildCertTrustManager(trustCertPath);
} else if (truststoreFile != null) {
trustManagers = buildTrustStoreTrustManager();
}
sc.init(null, trustManagers, new SecureRandom());
return sc.getSocketFactory();
} catch (Exception e) {
throw new SSLException("Https buildSSLSocketFactory error ", e);
}
}
}
因为certPath
为空,下面的条件式执行getTrustCertPath
方法,下一步就是执行getTruststoreFile
方法
两个方法源码如下:
private static String getTrustCertPath() {
return System.getProperty("aliyun.sdk.ssl.trustCertPath");
}
private static String getTruststoreFile() {
return System.getProperty("aliyun.sdk.ssl.truststoreFile");
}
由于我们并没又设置这两个系统属性,所有都获得为空
com.aliyuncs.utils.ParameterHelper#getFormData
|设置url参数,第一个参数不用&,后面所有参数前面都要有&
com.aliyuncs.auth.AcsURLEncoder#percentEncode
|短信服务那个签名扩展的编码方式