🎨 新增okhttp实现接口请求的单例模式代码

This commit is contained in:
WuLang 2023-02-18 22:52:02 +08:00 committed by GitHub
parent a47bc221f5
commit b83435281d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 733 additions and 23 deletions

View File

@ -0,0 +1,263 @@
package me.chanjar.weixin.common.util.http.okhttp;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import javax.annotation.concurrent.NotThreadSafe;
import java.net.Proxy;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @author wulang
**/
@Slf4j
@Data
@NotThreadSafe
public class DefaultOkHttpClientBuilder implements OkHttpClientBuilder {
private final AtomicBoolean prepared = new AtomicBoolean(false);
/**
* 代理
*/
private Proxy proxy;
/**
* 授权
*/
private Authenticator authenticator;
/**
* 拦截器
*/
private final List<Interceptor> interceptorList = new ArrayList<>();
/**
* 请求调度管理
*/
private Dispatcher dispatcher;
/**
* 连接池
*/
private ConnectionPool connectionPool;
/**
* 监听网络请求过程
*/
private EventListener.Factory eventListenerFactory;
/**
* 是否支持失败重连
*/
private Boolean retryOnConnectionFailure;
/**
* 是否允许重定向操作
*/
private Boolean followRedirects;
/**
* 连接建立的超时时长
*/
private Long connectTimeout;
/**
* 连接建立的超时时间单位
*/
private TimeUnit connectTimeUnit;
/**
* 完整的请求过程超时时长
*/
private Long callTimeout;
/**
* 完整的请求过程超时时间单位
*/
private TimeUnit callTimeUnit;
/**
* 连接的IO读操作超时时长
*/
private Long readTimeout;
/**
* 连接的IO读操作超时时间单位
*/
private TimeUnit readTimeUnit;
/**
* 连接的IO写操作超时时长
*/
private Long writeTimeout;
/**
* 连接的IO写操作超时时间单位
*/
private TimeUnit writeTimeUnit;
/**
* ping的时间间隔
*/
private Integer pingInterval;
/**
* 持有client对象,仅初始化一次,避免多service实例的时候造成重复初始化的问题
*/
private OkHttpClient okHttpClient;
private DefaultOkHttpClientBuilder() {
}
public static DefaultOkHttpClientBuilder get() {
return DefaultOkHttpClientBuilder.SingletonHolder.INSTANCE;
}
@Override
public OkHttpClient build() {
if (!prepared.get()) {
prepare();
}
return this.okHttpClient;
}
@Override
public OkHttpClientBuilder proxy(Proxy proxy) {
this.proxy = proxy;
return this;
}
@Override
public OkHttpClientBuilder authenticator(Authenticator authenticator) {
this.authenticator = authenticator;
return this;
}
@Override
public OkHttpClientBuilder addInterceptor(Interceptor interceptor) {
this.interceptorList.add(interceptor);
return this;
}
@Override
public OkHttpClientBuilder setDispatcher(Dispatcher dispatcher) {
this.dispatcher = dispatcher;
return this;
}
@Override
public OkHttpClientBuilder setConnectionPool(ConnectionPool connectionPool) {
this.connectionPool = connectionPool;
return this;
}
@Override
public OkHttpClientBuilder setEventListenerFactory(EventListener.Factory eventListenerFactory) {
this.eventListenerFactory = eventListenerFactory;
return this;
}
@Override
public OkHttpClientBuilder setRetryOnConnectionFailure(Boolean retryOnConnectionFailure) {
this.retryOnConnectionFailure = retryOnConnectionFailure;
return this;
}
@Override
public OkHttpClientBuilder setFollowRedirects(Boolean followRedirects) {
this.followRedirects = followRedirects;
return this;
}
@Override
public OkHttpClientBuilder connectTimeout(Long timeout, TimeUnit unit) {
this.connectTimeout = timeout;
this.connectTimeUnit = unit;
return this;
}
@Override
public OkHttpClientBuilder callTimeout(Long timeout, TimeUnit unit) {
this.callTimeout = timeout;
this.callTimeUnit = unit;
return this;
}
@Override
public OkHttpClientBuilder readTimeout(Long timeout, TimeUnit unit) {
this.readTimeout = timeout;
this.readTimeUnit = unit;
return this;
}
@Override
public OkHttpClientBuilder writeTimeout(Long timeout, TimeUnit unit) {
this.writeTimeout = timeout;
this.writeTimeUnit = unit;
return this;
}
@Override
public OkHttpClientBuilder setPingInterval(Integer pingInterval) {
this.pingInterval = pingInterval;
return this;
}
private synchronized void prepare() {
if (prepared.get()) {
return;
}
OkHttpClient.Builder builder = new OkHttpClient.Builder();
if (this.authenticator != null) {
builder.authenticator(this.authenticator);
}
if (this.proxy != null) {
builder.proxy(this.proxy);
}
for (Interceptor interceptor : this.interceptorList) {
builder.addInterceptor(interceptor);
}
if (this.dispatcher != null) {
builder.dispatcher(dispatcher);
}
if (this.connectionPool != null) {
builder.connectionPool(connectionPool);
}
if (this.eventListenerFactory != null) {
builder.eventListenerFactory(this.eventListenerFactory);
}
if (this.retryOnConnectionFailure != null) {
builder.setRetryOnConnectionFailure$okhttp(this.retryOnConnectionFailure);
}
if (this.followRedirects != null) {
builder.followRedirects(this.followRedirects);
}
if (this.connectTimeout != null && this.connectTimeUnit != null) {
builder.connectTimeout(this.connectTimeout, this.connectTimeUnit);
}
if (this.callTimeout != null && this.callTimeUnit != null) {
builder.callTimeout(this.callTimeout, this.callTimeUnit);
}
if (this.readTimeout != null && this.readTimeUnit != null) {
builder.readTimeout(this.readTimeout, this.readTimeUnit);
}
if (this.writeTimeout != null && this.writeTimeUnit != null) {
builder.writeTimeout(this.writeTimeout, this.writeTimeUnit);
}
if (this.pingInterval != null) {
builder.setPingInterval$okhttp(this.pingInterval);
}
this.okHttpClient = builder.build();
prepared.set(true);
}
private static class SingletonHolder {
private static final DefaultOkHttpClientBuilder INSTANCE = new DefaultOkHttpClientBuilder();
}
}

View File

@ -0,0 +1,126 @@
package me.chanjar.weixin.common.util.http.okhttp;
import okhttp3.*;
import java.net.Proxy;
import java.util.concurrent.TimeUnit;
/**
* @author wulang
**/
public interface OkHttpClientBuilder {
/**
* 构建OkHttpClient实例.
*
* @return OkHttpClient
*/
OkHttpClient build();
/**
* 代理
*
* @param proxy Proxy
* @return OkHttpClientBuilder
*/
OkHttpClientBuilder proxy(Proxy proxy);
/**
* 授权
*
* @param authenticator Authenticator
* @return OkHttpClientBuilder
*/
OkHttpClientBuilder authenticator(Authenticator authenticator);
/**
* 拦截器
*
* @param interceptor Interceptor
* @return OkHttpClientBuilder
*/
OkHttpClientBuilder addInterceptor(Interceptor interceptor);
/**
* 请求调度管理
*
* @param dispatcher Dispatcher
* @return OkHttpClientBuilder
*/
OkHttpClientBuilder setDispatcher(Dispatcher dispatcher);
/**
* 连接池
*
* @param connectionPool ConnectionPool
* @return OkHttpClientBuilder
*/
OkHttpClientBuilder setConnectionPool(ConnectionPool connectionPool);
/**
* 监听网络请求过程
*
* @param eventListenerFactory EventListener
* @return OkHttpClientBuilder
*/
OkHttpClientBuilder setEventListenerFactory(EventListener.Factory eventListenerFactory);
/**
* 是否支持失败重连
*
* @param retryOnConnectionFailure retryOnConnectionFailure
* @return OkHttpClientBuilder
*/
OkHttpClientBuilder setRetryOnConnectionFailure(Boolean retryOnConnectionFailure);
/**
* 是否允许重定向操作
*
* @param followRedirects followRedirects
* @return OkHttpClientBuilder
*/
OkHttpClientBuilder setFollowRedirects(Boolean followRedirects);
/**
* 连接建立的超时时间
*
* @param timeout 时长
* @param unit 时间单位
* @return OkHttpClientBuilder
*/
OkHttpClientBuilder connectTimeout(Long timeout, TimeUnit unit);
/**
* 完整的请求过程超时时间
*
* @param timeout 时长
* @param unit 时间单位
* @return OkHttpClientBuilder
*/
OkHttpClientBuilder callTimeout(Long timeout, TimeUnit unit);
/**
* 连接的IO读操作超时时间
*
* @param timeout 时长
* @param unit 时间单位
* @return OkHttpClientBuilder
*/
OkHttpClientBuilder readTimeout(Long timeout, TimeUnit unit);
/**
* 连接的IO写操作超时时间
*
* @param timeout 时长
* @param unit 时间单位
* @return OkHttpClientBuilder
*/
OkHttpClientBuilder writeTimeout(Long timeout, TimeUnit unit);
/**
* ping的时间间隔
*
* @param pingInterval ping的时间间隔
* @return OkHttpClientBuilder
*/
OkHttpClientBuilder setPingInterval(Integer pingInterval);
}

View File

@ -0,0 +1,257 @@
package me.chanjar.weixin.common.util.http.okhttp;
import okhttp3.*;
import java.net.Proxy;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* OkHttpClient 连接管理器 多一个DNS解析.
* <p>大部分代码拷贝自DefaultOkHttpClientBuilder</p>
*
* @author wulang
**/
public class OkHttpDnsClientBuilder implements OkHttpClientBuilder {
/**
* 代理
*/
private Proxy proxy;
/**
* 授权
*/
private Authenticator authenticator;
/**
* 拦截器
*/
private final List<Interceptor> interceptorList = new ArrayList<>();
/**
* 请求调度管理
*/
private Dispatcher dispatcher;
/**
* 连接池
*/
private ConnectionPool connectionPool;
/**
* 监听网络请求过程
*/
private EventListener.Factory eventListenerFactory;
/**
* 是否支持失败重连
*/
private Boolean retryOnConnectionFailure;
/**
* 是否允许重定向操作
*/
private Boolean followRedirects;
/**
* 连接建立的超时时长
*/
private Long connectTimeout;
/**
* 连接建立的超时时间单位
*/
private TimeUnit connectTimeUnit;
/**
* 完整的请求过程超时时长
*/
private Long callTimeout;
/**
* 完整的请求过程超时时间单位
*/
private TimeUnit callTimeUnit;
/**
* 连接的IO读操作超时时长
*/
private Long readTimeout;
/**
* 连接的IO读操作超时时间单位
*/
private TimeUnit readTimeUnit;
/**
* 连接的IO写操作超时时长
*/
private Long writeTimeout;
/**
* 连接的IO写操作超时时间单位
*/
private TimeUnit writeTimeUnit;
/**
* ping的时间间隔
*/
private Integer pingInterval;
private Dns dns;
private OkHttpClient okHttpClient;
private OkHttpDnsClientBuilder() {
}
public static OkHttpDnsClientBuilder get() {
return new OkHttpDnsClientBuilder();
}
public Dns getDns() {
return dns;
}
public void setDns(Dns dns) {
this.dns = dns;
}
@Override
public OkHttpClient build() {
prepare();
return this.okHttpClient;
}
@Override
public OkHttpClientBuilder proxy(Proxy proxy) {
this.proxy = proxy;
return this;
}
@Override
public OkHttpClientBuilder authenticator(Authenticator authenticator) {
this.authenticator = authenticator;
return this;
}
@Override
public OkHttpClientBuilder addInterceptor(Interceptor interceptor) {
this.interceptorList.add(interceptor);
return this;
}
@Override
public OkHttpClientBuilder setDispatcher(Dispatcher dispatcher) {
this.dispatcher = dispatcher;
return this;
}
@Override
public OkHttpClientBuilder setConnectionPool(ConnectionPool connectionPool) {
this.connectionPool = connectionPool;
return this;
}
@Override
public OkHttpClientBuilder setEventListenerFactory(EventListener.Factory eventListenerFactory) {
this.eventListenerFactory = eventListenerFactory;
return this;
}
@Override
public OkHttpClientBuilder setRetryOnConnectionFailure(Boolean retryOnConnectionFailure) {
this.retryOnConnectionFailure = retryOnConnectionFailure;
return this;
}
@Override
public OkHttpClientBuilder setFollowRedirects(Boolean followRedirects) {
this.followRedirects = followRedirects;
return this;
}
@Override
public OkHttpClientBuilder connectTimeout(Long timeout, TimeUnit unit) {
this.connectTimeout = timeout;
this.connectTimeUnit = unit;
return this;
}
@Override
public OkHttpClientBuilder callTimeout(Long timeout, TimeUnit unit) {
this.callTimeout = timeout;
this.callTimeUnit = unit;
return this;
}
@Override
public OkHttpClientBuilder readTimeout(Long timeout, TimeUnit unit) {
this.readTimeout = timeout;
this.readTimeUnit = unit;
return this;
}
@Override
public OkHttpClientBuilder writeTimeout(Long timeout, TimeUnit unit) {
this.writeTimeout = timeout;
this.writeTimeUnit = unit;
return this;
}
@Override
public OkHttpClientBuilder setPingInterval(Integer pingInterval) {
this.pingInterval = pingInterval;
return this;
}
private synchronized void prepare() {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
if (this.authenticator != null) {
builder.authenticator(this.authenticator);
}
if (this.proxy != null) {
builder.proxy(this.proxy);
}
for (Interceptor interceptor : this.interceptorList) {
builder.addInterceptor(interceptor);
}
if (this.dispatcher != null) {
builder.dispatcher(dispatcher);
}
if (this.connectionPool != null) {
builder.connectionPool(connectionPool);
}
if (this.eventListenerFactory != null) {
builder.eventListenerFactory(this.eventListenerFactory);
}
if (this.retryOnConnectionFailure != null) {
builder.setRetryOnConnectionFailure$okhttp(this.retryOnConnectionFailure);
}
if (this.followRedirects != null) {
builder.followRedirects(this.followRedirects);
}
if (this.dns != null) {
builder.dns(this.dns);
}
if (this.connectTimeout != null && this.connectTimeUnit != null) {
builder.connectTimeout(this.connectTimeout, this.connectTimeUnit);
}
if (this.callTimeout != null && this.callTimeUnit != null) {
builder.callTimeout(this.callTimeout, this.callTimeUnit);
}
if (this.readTimeout != null && this.readTimeUnit != null) {
builder.readTimeout(this.readTimeout, this.readTimeUnit);
}
if (this.writeTimeout != null && this.writeTimeUnit != null) {
builder.writeTimeout(this.writeTimeout, this.writeTimeUnit);
}
if (this.pingInterval != null) {
builder.setPingInterval$okhttp(this.pingInterval);
}
this.okHttpClient = builder.build();
}
}

View File

@ -0,0 +1,66 @@
package me.chanjar.weixin.common.util.http.okhttp;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.testng.Assert;
import org.testng.annotations.Test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class DefaultOkHttpClientBuilderTest {
@Test
public void testBuild() throws Exception {
DefaultOkHttpClientBuilder builder1 = DefaultOkHttpClientBuilder.get();
DefaultOkHttpClientBuilder builder2 = DefaultOkHttpClientBuilder.get();
Assert.assertSame(builder1, builder2, "DefaultOkHttpClientBuilder为单例,获取到的对象应该相同");
List<DefaultOkHttpClientBuilderTest.TestThread> threadList = new ArrayList<>(10);
for (int i = 0; i < 10; i++) {
DefaultOkHttpClientBuilderTest.TestThread thread = new DefaultOkHttpClientBuilderTest.TestThread();
thread.start();
threadList.add(thread);
}
for (DefaultOkHttpClientBuilderTest.TestThread testThread : threadList) {
testThread.join();
Assert.assertNotEquals(-1, testThread.getRespState(), "请求响应code不应为-1");
}
for (int i = 1; i < threadList.size(); i++) {
DefaultOkHttpClientBuilderTest.TestThread thread1 = threadList.get(i - 1);
DefaultOkHttpClientBuilderTest.TestThread thread2 = threadList.get(i);
Assert.assertSame(
thread1.getClient(),
thread2.getClient(),
"DefaultOkHttpClientBuilderTest为单例,并持有了相同的OkHttpClient"
);
}
}
public static class TestThread extends Thread {
private OkHttpClient client;
private int respState = -1;
@Override
public void run() {
client = DefaultOkHttpClientBuilder.get().build();
Request request = new Request.Builder()
.url("http://www.sina.com.cn/")
.build();
try (Response response = client.newCall(request).execute()) {
respState = response.code();
} catch (IOException e) {
// ignore
}
}
public OkHttpClient getClient() {
return client;
}
public int getRespState() {
return respState;
}
}
}

View File

@ -6,6 +6,7 @@ import me.chanjar.weixin.common.enums.WxType;
import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.http.HttpType;
import me.chanjar.weixin.common.util.http.okhttp.DefaultOkHttpClientBuilder;
import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo;
import me.chanjar.weixin.cp.config.WxCpConfigStorage;
import okhttp3.*;
@ -82,12 +83,8 @@ public class WxCpServiceOkHttpImpl extends BaseWxCpServiceImpl<OkHttpClient, OkH
configStorage.getHttpProxyPort(),
configStorage.getHttpProxyUsername(),
configStorage.getHttpProxyPassword());
}
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
if (httpProxy != null) {
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
clientBuilder.proxy(getRequestHttpProxy().getProxy());
//设置授权
clientBuilder.authenticator(new Authenticator() {
@Override
@ -98,8 +95,10 @@ public class WxCpServiceOkHttpImpl extends BaseWxCpServiceImpl<OkHttpClient, OkH
.build();
}
});
httpClient = clientBuilder.build();
} else {
httpClient = DefaultOkHttpClientBuilder.get().build();
}
httpClient = clientBuilder.build();
}
@Override

View File

@ -3,6 +3,7 @@ package cn.binarywang.wx.miniapp.api.impl;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import me.chanjar.weixin.common.util.http.HttpType;
import me.chanjar.weixin.common.util.http.okhttp.DefaultOkHttpClientBuilder;
import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo;
import okhttp3.*;
import org.apache.commons.lang3.StringUtils;
@ -27,12 +28,8 @@ public class WxMaServiceOkHttpImpl extends BaseWxMaServiceImpl<OkHttpClient, OkH
wxMpConfigStorage.getHttpProxyPort(),
wxMpConfigStorage.getHttpProxyUsername(),
wxMpConfigStorage.getHttpProxyPassword());
}
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
if (httpProxy != null) {
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
clientBuilder.proxy(getRequestHttpProxy().getProxy());
//设置授权
clientBuilder.authenticator(new Authenticator() {
@Override
@ -43,8 +40,10 @@ public class WxMaServiceOkHttpImpl extends BaseWxMaServiceImpl<OkHttpClient, OkH
.build();
}
});
httpClient = clientBuilder.build();
} else {
httpClient = DefaultOkHttpClientBuilder.get().build();
}
httpClient = clientBuilder.build();
}
@Override

View File

@ -3,6 +3,7 @@ package me.chanjar.weixin.mp.api.impl;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.error.WxRuntimeException;
import me.chanjar.weixin.common.util.http.HttpType;
import me.chanjar.weixin.common.util.http.okhttp.DefaultOkHttpClientBuilder;
import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo;
import me.chanjar.weixin.mp.config.WxMpConfigStorage;
import okhttp3.*;
@ -79,10 +80,7 @@ public class WxMpServiceOkHttpImpl extends BaseWxMpServiceImpl<OkHttpClient, OkH
wxMpConfigStorage.getHttpProxyPort(),
wxMpConfigStorage.getHttpProxyUsername(),
wxMpConfigStorage.getHttpProxyPassword());
}
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
if (httpProxy != null) {
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
clientBuilder.proxy(getRequestHttpProxy().getProxy());
//设置授权
@ -95,8 +93,10 @@ public class WxMpServiceOkHttpImpl extends BaseWxMpServiceImpl<OkHttpClient, OkH
.build();
}
});
httpClient = clientBuilder.build();
} else {
httpClient = DefaultOkHttpClientBuilder.get().build();
}
httpClient = clientBuilder.build();
}
}

View File

@ -3,6 +3,7 @@ package me.chanjar.weixin.qidian.api.impl;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.error.WxRuntimeException;
import me.chanjar.weixin.common.util.http.HttpType;
import me.chanjar.weixin.common.util.http.okhttp.DefaultOkHttpClientBuilder;
import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo;
import me.chanjar.weixin.qidian.config.WxQidianConfigStorage;
import okhttp3.*;
@ -76,11 +77,8 @@ public class WxQidianServiceOkHttpImpl extends BaseWxQidianServiceImpl<OkHttpCli
// 设置代理
if (wxMpConfigStorage.getHttpProxyHost() != null && wxMpConfigStorage.getHttpProxyPort() > 0) {
httpProxy = OkHttpProxyInfo.httpProxy(wxMpConfigStorage.getHttpProxyHost(), wxMpConfigStorage.getHttpProxyPort(),
wxMpConfigStorage.getHttpProxyUsername(), wxMpConfigStorage.getHttpProxyPassword());
}
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
if (httpProxy != null) {
wxMpConfigStorage.getHttpProxyUsername(), wxMpConfigStorage.getHttpProxyPassword());
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
clientBuilder.proxy(getRequestHttpProxy().getProxy());
// 设置授权
@ -91,8 +89,10 @@ public class WxQidianServiceOkHttpImpl extends BaseWxQidianServiceImpl<OkHttpCli
return response.request().newBuilder().header("Authorization", credential).build();
}
});
httpClient = clientBuilder.build();
} else {
httpClient = DefaultOkHttpClientBuilder.get().build();
}
httpClient = clientBuilder.build();
}
}