diff --git a/spring-web/src/main/java/org/springframework/http/client/support/HttpAccessor.java b/spring-web/src/main/java/org/springframework/http/client/support/HttpAccessor.java index 6904445fab6..3a3d02cc9c1 100644 --- a/spring-web/src/main/java/org/springframework/http/client/support/HttpAccessor.java +++ b/spring-web/src/main/java/org/springframework/http/client/support/HttpAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,11 +34,12 @@ import org.springframework.util.Assert; * such as the {@link ClientHttpRequestFactory} to operate on. * *

Not intended to be used directly. - * See {@link org.springframework.web.client.RestTemplate}. + * See {@link org.springframework.web.client.RestTemplate} for an entry point. * * @author Arjen Poutsma * @author Juergen Hoeller * @since 3.0 + * @see ClientHttpRequestFactory * @see org.springframework.web.client.RestTemplate */ public abstract class HttpAccessor { @@ -56,6 +57,7 @@ public abstract class HttpAccessor { *

Note that the standard JDK HTTP library does not support the HTTP PATCH method. * Configure the Apache HttpComponents or OkHttp request factory to enable PATCH. * @see #createRequest(URI, HttpMethod) + * @see SimpleClientHttpRequestFactory * @see org.springframework.http.client.HttpComponentsAsyncClientHttpRequestFactory * @see org.springframework.http.client.OkHttp3ClientHttpRequestFactory */ diff --git a/spring-web/src/main/java/org/springframework/http/client/support/InterceptingHttpAccessor.java b/spring-web/src/main/java/org/springframework/http/client/support/InterceptingHttpAccessor.java index ecb28598a3b..0147996b03a 100644 --- a/spring-web/src/main/java/org/springframework/http/client/support/InterceptingHttpAccessor.java +++ b/spring-web/src/main/java/org/springframework/http/client/support/InterceptingHttpAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,43 +23,83 @@ import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.InterceptingClientHttpRequestFactory; +import org.springframework.lang.Nullable; import org.springframework.util.CollectionUtils; /** - * Base class for {@link org.springframework.web.client.RestTemplate} and other HTTP accessing gateway helpers, adding - * interceptor-related properties to {@link HttpAccessor}'s common properties. + * Base class for {@link org.springframework.web.client.RestTemplate} + * and other HTTP accessing gateway helpers, adding interceptor-related + * properties to {@link HttpAccessor}'s common properties. * - *

Not intended to be used directly. See {@link org.springframework.web.client.RestTemplate}. + *

Not intended to be used directly. + * See {@link org.springframework.web.client.RestTemplate} for an entry point. * * @author Arjen Poutsma + * @author Juergen Hoeller + * @since 3.0 + * @see ClientHttpRequestInterceptor + * @see InterceptingClientHttpRequestFactory + * @see org.springframework.web.client.RestTemplate */ public abstract class InterceptingHttpAccessor extends HttpAccessor { - private List interceptors = new ArrayList<>(); + private final List interceptors = new ArrayList<>(); + + @Nullable + private volatile ClientHttpRequestFactory interceptingRequestFactory; + /** - * Sets the request interceptors that this accessor should use. + * Set the request interceptors that this accessor should use. + *

The interceptors will get sorted according to their order + * once the {@link ClientHttpRequestFactory} will be built. + * @see #getRequestFactory() + * @see AnnotationAwareOrderComparator */ public void setInterceptors(List interceptors) { - AnnotationAwareOrderComparator.sort(interceptors); - this.interceptors = interceptors; + // Take getInterceptors() List as-is when passed in here + if (this.interceptors != interceptors) { + this.interceptors.clear(); + this.interceptors.addAll(interceptors); + AnnotationAwareOrderComparator.sort(this.interceptors); + } } /** - * Return the request interceptor that this accessor uses. + * Return the request interceptors that this accessor uses. + *

The returned {@link List} is active and may get appended to. */ public List getInterceptors() { - return interceptors; + return this.interceptors; } + /** + * {@inheritDoc} + */ + @Override + public void setRequestFactory(ClientHttpRequestFactory requestFactory) { + super.setRequestFactory(requestFactory); + this.interceptingRequestFactory = null; + } + + /** + * Overridden to expose an {@link InterceptingClientHttpRequestFactory} + * if necessary. + * @see #getInterceptors() + */ @Override public ClientHttpRequestFactory getRequestFactory() { - ClientHttpRequestFactory delegate = super.getRequestFactory(); - if (!CollectionUtils.isEmpty(getInterceptors())) { - return new InterceptingClientHttpRequestFactory(delegate, getInterceptors()); + List interceptors = getInterceptors(); + if (!CollectionUtils.isEmpty(interceptors)) { + ClientHttpRequestFactory factory = this.interceptingRequestFactory; + if (factory == null) { + factory = new InterceptingClientHttpRequestFactory(super.getRequestFactory(), interceptors); + this.interceptingRequestFactory = factory; + } + return factory; } else { - return delegate; + return super.getRequestFactory(); } } diff --git a/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java b/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java index 3aec0538e1f..4acd8ae02eb 100644 --- a/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java +++ b/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java @@ -237,7 +237,8 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat } /** - * Return the message body converters. + * Return the list of message body converters. + *

The returned {@link List} is active and may get appended to. */ public List> getMessageConverters() { return this.messageConverters; diff --git a/spring-web/src/test/java/org/springframework/http/client/support/InterceptingHttpAccessorTests.java b/spring-web/src/test/java/org/springframework/http/client/support/InterceptingHttpAccessorTests.java index 0e515883a31..2bd99f98280 100644 --- a/spring-web/src/test/java/org/springframework/http/client/support/InterceptingHttpAccessorTests.java +++ b/spring-web/src/test/java/org/springframework/http/client/support/InterceptingHttpAccessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,9 +16,6 @@ package org.springframework.http.client.support; -import static org.junit.Assert.*; - -import java.io.IOException; import java.util.Arrays; import java.util.List; @@ -32,6 +29,8 @@ import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; +import static org.junit.Assert.*; + /** * Tests for {@link InterceptingHttpAccessor}. * @@ -40,7 +39,7 @@ import org.springframework.http.client.ClientHttpResponse; public class InterceptingHttpAccessorTests { @Test - public void getInterceptors() throws Exception { + public void getInterceptors() { TestInterceptingHttpAccessor accessor = new TestInterceptingHttpAccessor(); List interceptors = Arrays.asList( new SecondClientHttpRequestInterceptor(), @@ -55,22 +54,25 @@ public class InterceptingHttpAccessorTests { assertThat(accessor.getInterceptors().get(2), Matchers.instanceOf(ThirdClientHttpRequestInterceptor.class)); } + private class TestInterceptingHttpAccessor extends InterceptingHttpAccessor { } + @Order(1) private class FirstClientHttpRequestInterceptor implements ClientHttpRequestInterceptor { + @Override - public ClientHttpResponse intercept(HttpRequest request, byte[] body, - ClientHttpRequestExecution execution) throws IOException { + public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) { return null; } } + private class SecondClientHttpRequestInterceptor implements ClientHttpRequestInterceptor, Ordered { + @Override - public ClientHttpResponse intercept(HttpRequest request, byte[] body, - ClientHttpRequestExecution execution) throws IOException { + public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) { return null; } @@ -80,12 +82,13 @@ public class InterceptingHttpAccessorTests { } } + private class ThirdClientHttpRequestInterceptor implements ClientHttpRequestInterceptor { + @Override - public ClientHttpResponse intercept(HttpRequest request, byte[] body, - ClientHttpRequestExecution execution) throws IOException { + public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) { return null; } } -} \ No newline at end of file +}