Groovy @Category(Object) 注解:构建基于 HttpClient 的自定义 DSL,实现 HTTP 请求方法
Groovy 的 @Category(Object) 注解用于将一个 Groovy 类或对象作为扩展类或对象添加到现有的 Java 类或对象中。这样可以扩展现有类的功能或添加新的方法。
示例说明:
假设有一个 Java 类 Person,我们可以使用 Groovy 的 @Category 注解来添加一个扩展类,增加一个方法 sayHello:
class Person {
String name
Person(String name) {
this.name = name
}
}
@Category(Person)
class PersonExtension {
String sayHello() {
'Hello, ${delegate.name}!'
}
}
def person = new Person('Jack')
assert person.sayHello() == 'Hello, Jack!'
在这个例子中,我们使用 @Category 注解将 PersonExtension 类作为 Person 类的扩展类添加进去。可以通过实例化 Person 类并调用 sayHello 方法来得到输出结果。
基于 HttpClient 的自定义 DSL
现在,我们来编写一个基于 HttpClient 的自定义 DSL 来实现 get、post、delete 等请求方法,内部管理一个连接池,支持 Http 和 Https 协议,单个请求支持是否使用代理,请求异常时自动重试,入参和出参类型转换等功能。
首先,我们需要引入 HttpClient 的依赖:
@Grab(group='org.apache.httpcomponents', module='httpclient', version='4.5.13')
然后,我们可以使用 Groovy 的 DSL 语法来编写 HttpRequest 类:
class HttpRequest {
private final CloseableHttpClient httpClient
private final String url
private final HttpMethod method
private final ContentType contentType
private final Map<String, String> headers
private final Map<String, Object> params
private final boolean useProxy
private final int maxRetry
private final int retryInterval
private final Map<Class, Class> convertTypeMap
HttpRequest(CloseableHttpClient httpClient, String url, HttpMethod method, ContentType contentType,
Map<String, String> headers, Map<String, Object> params, boolean useProxy, int maxRetry,
int retryInterval, Map<Class, Class> convertTypeMap) {
this.httpClient = httpClient
this.url = url
this.method = method
this.contentType = contentType
this.headers = headers ?: [:]
this.params = params ?: [:]
this.useProxy = useProxy
this.maxRetry = maxRetry ?: 0
this.retryInterval = retryInterval ?: 0
this.convertTypeMap = convertTypeMap ?: [:]
}
def execute() {
def request = new HttpUriRequestBuilder(method, url)
.contentType(contentType)
.headers(headers)
.params(params)
.useProxy(useProxy)
.build()
def retryCount = 0
while (retryCount <= maxRetry) {
try {
def response = httpClient.execute(request)
def entity = response.entity
def statusCode = response.statusLine.statusCode
if (statusCode >= HttpStatus.SC_OK && statusCode < HttpStatus.SC_MULTIPLE_CHOICES) {
def content = entity.content
def contentType = entity.contentType
def charset = contentType?.charset?.name() ?: 'UTF-8'
def result = content.text(charset)
if (convertTypeMap.containsKey(result?.getClass())) {
result = result as convertTypeMap[result.getClass()]
}
return result
} else {
throw new RuntimeException('HTTP request failed with status code ${statusCode}')
}
} catch (Exception e) {
if (retryCount < maxRetry) {
retryCount++
Thread.sleep(retryInterval)
} else {
throw e
}
}
}
}
}
在这个类中,我们使用了 Groovy 的默认参数和 Map 默认值的特性,使得实例化 HttpRequest 类时可以省略一些参数。execute 方法中,我们使用了 HttpClient 的 CloseableHttpClient、HttpUriRequestBuilder 和 HttpResponse 等类来执行 HTTP 请求,并添加了重试和类型转换等功能。
接下来,我们可以编写一个 DSL 类 HttpDSL,提供 get、post、delete 等方法,以及连接池、代理、重试、类型转换等配置:
class HttpDSL {
private final CloseableHttpClient httpClient
private final boolean usePool
private final int maxPoolSize
private final int connectionTimeout
private final int socketTimeout
private final Map<String, Object> defaultParams
private final boolean useProxy
private final String proxyHost
private final int proxyPort
private final int maxRetry
private final int retryInterval
private final Map<Class, Class> convertTypeMap
HttpDSL(boolean usePool = true, int maxPoolSize = 50, int connectionTimeout = 5000, int socketTimeout = 5000,
Map<String, Object> defaultParams = [:], boolean useProxy = false, String proxyHost = null, int proxyPort = 0,
int maxRetry = 0, int retryInterval = 5000, Map<Class, Class> convertTypeMap = [:]) {
def builder = HttpClientBuilder.create()
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectTimeout(connectionTimeout)
.setSocketTimeout(socketTimeout)
.build())
if (usePool) {
builder.setConnectionManager(new PoolingHttpClientConnectionManager(
RegistryBuilder.<ConnectionSocketFactory>create()
.register('http', PlainConnectionSocketFactory.getSocketFactory())
.register('https', SSLConnectionSocketFactory.getSocketFactory())
.build()))
.setMaxConnTotal(maxPoolSize)
.setMaxConnPerRoute(maxPoolSize)
}
httpClient = builder.build()
this.usePool = usePool
this.maxPoolSize = maxPoolSize
this.connectionTimeout = connectionTimeout
this.socketTimeout = socketTimeout
this.defaultParams = defaultParams
this.useProxy = useProxy
this.proxyHost = proxyHost
this.proxyPort = proxyPort
this.maxRetry = maxRetry
this.retryInterval = retryInterval
this.convertTypeMap = convertTypeMap
}
def get(String url, Map<String, Object> params = [:], Map<String, String> headers = [:], ContentType contentType = null) {
new HttpRequest(httpClient, url, HttpMethod.GET, contentType, headers,
defaultParams + params, useProxy, maxRetry, retryInterval, convertTypeMap)
.execute()
}
def post(String url, Map<String, Object> params = [:], Map<String, String> headers = [:], ContentType contentType = null) {
new HttpRequest(httpClient, url, HttpMethod.POST, contentType, headers,
defaultParams + params, useProxy, maxRetry, retryInterval, convertTypeMap)
.execute()
}
def delete(String url, Map<String, Object> params = [:], Map<String, String> headers = [:], ContentType contentType = null) {
new HttpRequest(httpClient, url, HttpMethod.DELETE, contentType, headers,
defaultParams + params, useProxy, maxRetry, retryInterval, convertTypeMap)
.execute()
}
def useProxy(String host, int port) {
new HttpDSL(usePool, maxPoolSize, connectionTimeout, socketTimeout, defaultParams,
true, host, port, maxRetry, retryInterval, convertTypeMap)
}
def maxRetry(int count) {
new HttpDSL(usePool, maxPoolSize, connectionTimeout, socketTimeout, defaultParams,
useProxy, proxyHost, proxyPort, count, retryInterval, convertTypeMap)
}
def retryInterval(int interval) {
new HttpDSL(usePool, maxPoolSize, connectionTimeout, socketTimeout, defaultParams,
useProxy, proxyHost, proxyPort, maxRetry, interval, convertTypeMap)
}
def convertType(Class fromType, Class toType) {
new HttpDSL(usePool, maxPoolSize, connectionTimeout, socketTimeout, defaultParams,
useProxy, proxyHost, proxyPort, maxRetry, retryInterval, convertTypeMap + [(fromType): toType])
}
def setDefaultParams(Map<String, Object> params) {
new HttpDSL(usePool, maxPoolSize, connectionTimeout, socketTimeout, params,
useProxy, proxyHost, proxyPort, maxRetry, retryInterval, convertTypeMap)
}
def close() {
httpClient.close()
}
}
在这个类中,我们使用了 Groovy 的方法链式调用和默认参数的特性,使得配置 HttpDSL 对象时可以省略一些参数。get、post、delete 等方法中,我们使用了 HttpRequest 类来执行 HTTP 请求。useProxy、maxRetry、retryInterval、convertType 和 setDefaultParams 等方法来配置连接池、代理、重试、类型转换和默认参数等选项。close 方法用于关闭 HttpClient 的连接池。
现在,我们可以在 Groovy 脚本中使用 HttpDSL 来执行 HTTP 请求,例如:
def http = new HttpDSL(usePool: true, maxPoolSize: 50, maxRetry: 3)
def result = http.get('http://example.com', [q: 'groovy'])
println result
http.close()
在这个例子中,我们使用 HttpDSL 类来创建一个 HttpClient 对象,并使用 get 方法来执行 HTTP GET 请求。请求参数包括查询字符串 q=groovy,返回的结果会直接输出到控制台。最后,我们需要调用 close 方法来关闭 HttpClient 对象。
这样,我们就成功地使用 Groovy DSL 来实现了一个基于 HttpClient 的 HTTP 请求库,支持连接池、代理、重试、类型转换等功能。
原文地址: https://www.cveoy.top/t/topic/ojLJ 著作权归作者所有。请勿转载和采集!