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 的 CloseableHttpClientHttpUriRequestBuilderHttpResponse 等类来执行 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 对象时可以省略一些参数。getpostdelete 等方法中,我们使用了 HttpRequest 类来执行 HTTP 请求。useProxymaxRetryretryIntervalconvertTypesetDefaultParams 等方法来配置连接池、代理、重试、类型转换和默认参数等选项。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 请求库,支持连接池、代理、重试、类型转换等功能。

Groovy @Category(Object) 注解:构建基于 HttpClient 的自定义 DSL,实现 HTTP 请求方法

原文地址: https://www.cveoy.top/t/topic/ojLJ 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录