#977 - Performance improvements in link generation.

Introduced more caches for reusable value objects: UriTemplate, MethodParameters. Cache UriTemplate.toString() to avoid re-computation. Avoid recomputing the affordances if possible in WebHandler. Removed cache in AnnotatedParametersParameterAccessor as that one now transitively benefits from the one introduced in MethodParameters.

Related ticket: spring-projects/spring-framework#22730.
This commit is contained in:
Oliver Drotbohm
2019-04-03 18:31:46 +02:00
parent fc3767b90b
commit 39404ea2d9
15 changed files with 152 additions and 137 deletions

View File

@@ -118,7 +118,7 @@ public class Link implements Serializable {
* @param rel must not be {@literal null} or empty.
*/
public Link(String href, String rel) {
this(new UriTemplate(href), LinkRelation.of(rel));
this(UriTemplate.of(href), LinkRelation.of(rel));
}
/**
@@ -128,7 +128,7 @@ public class Link implements Serializable {
* @param rel must not be {@literal null} or empty.
*/
public Link(String href, LinkRelation rel) {
this(new UriTemplate(href), rel);
this(UriTemplate.of(href), rel);
}
/**
@@ -299,7 +299,7 @@ public class Link implements Serializable {
*/
@JsonIgnore
public List<String> getVariableNames() {
return getUriTemplate().getVariableNames();
return template.getVariableNames();
}
/**
@@ -309,7 +309,7 @@ public class Link implements Serializable {
*/
@JsonIgnore
public List<TemplateVariable> getVariables() {
return getUriTemplate().getVariables();
return template.getVariables();
}
/**
@@ -318,7 +318,7 @@ public class Link implements Serializable {
* @return
*/
public boolean isTemplated() {
return !getUriTemplate().getVariables().isEmpty();
return !template.getVariables().isEmpty();
}
/**
@@ -328,7 +328,7 @@ public class Link implements Serializable {
* @return
*/
public Link expand(Object... arguments) {
return new Link(getUriTemplate().expand(arguments).toString(), getRel());
return new Link(template.expand(arguments).toString(), getRel());
}
/**
@@ -338,7 +338,7 @@ public class Link implements Serializable {
* @return
*/
public Link expand(Map<String, ? extends Object> arguments) {
return new Link(getUriTemplate().expand(arguments).toString(), getRel());
return new Link(template.expand(arguments).toString(), getRel());
}
/**
@@ -390,15 +390,6 @@ public class Link implements Serializable {
return this.rel.isSameAs(rel);
}
private UriTemplate getUriTemplate() {
if (this.template == null) {
this.template = new UriTemplate(href);
}
return this.template;
}
/**
* Returns the current href as URI after expanding the links without any arguments, i.e. all optional URI
* {@link TemplateVariable}s will be dropped. If the href contains mandatory {@link TemplateVariable}s, the URI

View File

@@ -22,6 +22,7 @@ import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -46,7 +47,10 @@ public class UriTemplate implements Iterable<TemplateVariable>, Serializable {
private static final Pattern VARIABLE_REGEX = Pattern.compile("\\{([\\?\\&#/]?)([\\w\\,*]+)\\}");
private static final long serialVersionUID = -1007874653930162262L;
private static final Map<String, UriTemplate> CACHE = new ConcurrentHashMap<>();
private final TemplateVariables variables;
private String toString;
private String baseUri;
/**
@@ -54,7 +58,7 @@ public class UriTemplate implements Iterable<TemplateVariable>, Serializable {
*
* @param template must not be {@literal null} or empty.
*/
public UriTemplate(String template) {
private UriTemplate(String template) {
Assert.hasText(template, "Template must not be null or empty!");
@@ -97,7 +101,7 @@ public class UriTemplate implements Iterable<TemplateVariable>, Serializable {
* @param baseUri must not be {@literal null} or empty.
* @param variables must not be {@literal null}.
*/
public UriTemplate(String baseUri, TemplateVariables variables) {
UriTemplate(String baseUri, TemplateVariables variables) {
Assert.hasText(baseUri, "Base URI must not be null or empty!");
Assert.notNull(variables, "Template variables must not be null!");
@@ -106,6 +110,19 @@ public class UriTemplate implements Iterable<TemplateVariable>, Serializable {
this.variables = variables;
}
/**
* Returns a {@link UriTemplate} for the given {@link String} template.
*
* @param template must not be {@literal null} or empty.
* @return
*/
public static UriTemplate of(String template) {
Assert.hasText(template, "Template must not be null or empty!");
return CACHE.computeIfAbsent(template, UriTemplate::new);
}
/**
* Creates a new {@link UriTemplate} with the current {@link TemplateVariable}s augmented with the given ones.
*
@@ -267,10 +284,15 @@ public class UriTemplate implements Iterable<TemplateVariable>, Serializable {
@Override
public String toString() {
UriComponents components = UriComponentsBuilder.fromUriString(baseUri).build();
boolean hasQueryParameters = !components.getQueryParams().isEmpty();
if (toString == null) {
return baseUri + getOptionalVariables().toString(hasQueryParameters);
UriComponents components = UriComponentsBuilder.fromUriString(baseUri).build();
boolean hasQueryParameters = !components.getQueryParams().isEmpty();
this.toString = baseUri + getOptionalVariables().toString(hasQueryParameters);
}
return toString;
}
private TemplateVariables getOptionalVariables() {

View File

@@ -15,7 +15,7 @@
*/
package org.springframework.hateoas.client;
import static org.springframework.http.HttpMethod.GET;
import static org.springframework.http.HttpMethod.*;
import lombok.RequiredArgsConstructor;
import lombok.Value;
@@ -385,15 +385,14 @@ public class Traverson {
UriStringAndHeaders uriAndHeaders = getAndFindLinkWithRel(baseUri.toString(), rels.iterator(), HttpHeaders.EMPTY);
return new UriStringAndHeaders(new UriTemplate(uriAndHeaders.getUri()).toString(),
uriAndHeaders.getHttpHeaders());
return new UriStringAndHeaders(UriTemplate.of(uriAndHeaders.getUri()).toString(), uriAndHeaders.getHttpHeaders());
}
private URIAndHeaders traverseToExpandedFinalUrl() {
UriStringAndHeaders uriAndHeaders = getAndFindLinkWithRel(baseUri.toString(), rels.iterator(), HttpHeaders.EMPTY);
return new URIAndHeaders(new UriTemplate(uriAndHeaders.getUri()).expand(templateParameters),
return new URIAndHeaders(UriTemplate.of(uriAndHeaders.getUri()).expand(templateParameters),
uriAndHeaders.getHttpHeaders());
}
@@ -404,7 +403,7 @@ public class Traverson {
}
HttpEntity<?> request = prepareRequest(mergeHeaders(this.headers, extraHeaders));
URI target = new UriTemplate(uri).expand();
URI target = UriTemplate.of(uri).expand();
ResponseEntity<String> responseEntity = operations.exchange(target, GET, request, String.class);
MediaType contentType = responseEntity.getHeaders().getContentType();

View File

@@ -38,7 +38,8 @@ import org.springframework.util.ConcurrentReferenceHashMap;
*/
public class MethodParameters {
private static ParameterNameDiscoverer DISCOVERER = new DefaultParameterNameDiscoverer();
private static final ParameterNameDiscoverer DISCOVERER = new DefaultParameterNameDiscoverer();
private static final Map<Method, MethodParameters> CACHE = new ConcurrentReferenceHashMap<>();
private final List<MethodParameter> parameters;
private final Map<Class<?>, List<MethodParameter>> parametersWithAnnotationCache = new ConcurrentReferenceHashMap<>();
@@ -48,10 +49,23 @@ public class MethodParameters {
*
* @param method must not be {@literal null}.
*/
public MethodParameters(Method method) {
private MethodParameters(Method method) {
this(method, null);
}
/**
* Returns the {@link MethodParameters} for the given {@link Method}.
*
* @param method must not be {@literal null}.
* @return
*/
public static MethodParameters of(Method method) {
Assert.notNull(method, "Method must not be null!");
return CACHE.computeIfAbsent(method, MethodParameters::new);
}
/**
* Creates a new {@link MethodParameters} for the given {@link Method} and {@link AnnotationAttribute}. If the latter
* is given, method parameter names will be looked up from the annotation attribute if present.
@@ -115,14 +129,15 @@ public class MethodParameters {
* @param annotation must not be {@literal null}.
* @return
*/
@SuppressWarnings("unchecked")
public List<MethodParameter> getParametersWith(Class<? extends Annotation> annotation) {
Assert.notNull(annotation, "Annotation must not be null!");
return parametersWithAnnotationCache.computeIfAbsent(annotation, key -> {
Assert.notNull(annotation, "Annotation must not be null!");
return getParameters().stream()//
.filter(it -> it.hasParameterAnnotation(annotation))//
.filter(it -> it.hasParameterAnnotation((Class<? extends Annotation>) key))//
.collect(Collectors.toList());
});
}

View File

@@ -15,7 +15,7 @@
*/
package org.springframework.hateoas.server.core;
import java.util.ArrayList;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@@ -26,10 +26,8 @@ import org.springframework.hateoas.AffordanceModelFactory;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.LinkRelation;
import org.springframework.hateoas.QueryParameter;
import org.springframework.http.HttpMethod;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.util.UriComponents;
/**
* Extract information needed to assemble an {@link Affordance} from a Spring MVC web method.
@@ -43,44 +41,34 @@ public class SpringAffordanceBuilder {
* Use the attributes of the current method call along with a collection of {@link AffordanceModelFactory}'s to create
* a set of {@link Affordance}s.
*
* @param invocation
* @param discoverer
* @param components
* @param type must not be {@literal null}.
* @param method must not be {@literal null}.
* @param href must not be {@literal null}.
* @param discoverer must not be {@literal null}.
* @return
*/
public static List<Affordance> create(MethodInvocation invocation, MappingDiscoverer discoverer,
UriComponents components) {
public static List<Affordance> create(Class<?> type, Method method, String href, MappingDiscoverer discoverer) {
List<Affordance> affordances = new ArrayList<>();
String methodName = method.getName();
Link affordanceLink = new Link(href, LinkRelation.of(methodName));
for (HttpMethod requestMethod : discoverer.getRequestMethod(invocation.getTargetType(), invocation.getMethod())) {
MethodParameters parameters = MethodParameters.of(method);
String methodName = invocation.getMethod().getName();
ResolvableType inputType = parameters.getParametersWith(RequestBody.class).stream() //
.findFirst() //
.map(ResolvableType::forMethodParameter) //
.orElse(ResolvableType.NONE);
String href = components.toUriString().equals("") ? "/" : components.toUriString();
Link affordanceLink = new Link(href).withRel(LinkRelation.of(methodName));
List<QueryParameter> queryMethodParameters = parameters.getParametersWith(RequestParam.class).stream() //
.map(it -> it.getParameterAnnotation(RequestParam.class)) //
.filter(Objects::nonNull) //
.map(it -> new QueryParameter(it.name(), it.value(), it.required())) //
.collect(Collectors.toList());
MethodParameters invocationMethodParameters = new MethodParameters(invocation.getMethod());
ResolvableType outputType = ResolvableType.forMethodReturnType(method);
ResolvableType inputType = invocationMethodParameters.getParametersWith(RequestBody.class).stream() //
.findFirst() //
.map(ResolvableType::forMethodParameter) //
.orElse(ResolvableType.NONE);
List<QueryParameter> queryMethodParameters = invocationMethodParameters.getParametersWith(RequestParam.class)
.stream() //
.map(methodParameter -> methodParameter.getParameterAnnotation(RequestParam.class)) //
.filter(Objects::nonNull) //
.map(requestParam -> new QueryParameter(requestParam.name(), requestParam.value(), requestParam.required())) //
.collect(Collectors.toList());
ResolvableType outputType = ResolvableType.forMethodReturnType(invocation.getMethod());
affordances
.add(new Affordance(methodName, affordanceLink, requestMethod, inputType, queryMethodParameters, outputType));
}
return affordances;
return discoverer.getRequestMethod(type, method).stream() //
.map(it -> new Affordance(methodName, affordanceLink, it, inputType, queryMethodParameters, outputType)) //
.collect(Collectors.toList());
}
}

View File

@@ -22,6 +22,7 @@ import static org.springframework.web.util.UriComponents.UriTemplateVariables.*;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.Value;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
@@ -70,6 +71,8 @@ public class WebHandler {
private static final AnnotatedParametersParameterAccessor REQUEST_PARAM_ACCESSOR //
= new RequestParamParameterAccessor();
private static final Map<AffordanceKey, List<Affordance>> AFFORDANCES_CACHE = new ConcurrentReferenceHashMap<>();
public interface LinkBuilderCreator<T extends LinkBuilder> {
T createBuilder(UriComponents components, TemplateVariables variables, List<Affordance> affordances);
}
@@ -89,6 +92,7 @@ public class WebHandler {
MethodInvocation invocation = invocations.getLastInvocation();
return mappingToUriComponentsBuilder -> {
String mapping = DISCOVERER.getMapping(invocation.getTargetType(), invocation.getMethod());
UriComponentsBuilder builder = mappingToUriComponentsBuilder.apply(mapping);
@@ -145,7 +149,11 @@ public class WebHandler {
variables = variables.concat(variable);
}
List<Affordance> affordances = SpringAffordanceBuilder.create(invocation, DISCOVERER, components);
String href = components.toUriString().equals("") ? "/" : components.toUriString();
List<Affordance> affordances = AFFORDANCES_CACHE.computeIfAbsent(
AffordanceKey.of(invocation.getTargetType(), invocation.getMethod(), href),
key -> SpringAffordanceBuilder.create(key.type, key.method, key.href, DISCOVERER));
return creator.createBuilder(components, variables, affordances);
};
@@ -280,9 +288,6 @@ public class WebHandler {
@RequiredArgsConstructor
private static class AnnotatedParametersParameterAccessor {
private static final Map<Method, MethodParameters> METHOD_PARAMETERS_CACHE = new ConcurrentReferenceHashMap<>(16,
ConcurrentReferenceHashMap.ReferenceType.WEAK);
private final @NonNull AnnotationAttribute attribute;
/**
@@ -295,7 +300,7 @@ public class WebHandler {
Assert.notNull(invocation, "MethodInvocation must not be null!");
MethodParameters parameters = getOrCreateMethodParametersFor(invocation.getMethod());
MethodParameters parameters = MethodParameters.of(invocation.getMethod());
Object[] arguments = invocation.getArguments();
List<BoundMethodParameter> result = new ArrayList<>();
@@ -339,16 +344,6 @@ public class WebHandler {
return value;
}
/**
* Returns the {@link MethodParameters} for the given {@link Method}.
*
* @param method
* @return
*/
private static MethodParameters getOrCreateMethodParametersFor(Method method) {
return METHOD_PARAMETERS_CACHE.computeIfAbsent(method, MethodParameters::new);
}
/**
* Represents a {@link MethodParameter} alongside the value it has been bound to.
*
@@ -442,4 +437,12 @@ public class WebHandler {
}
}
}
@Value(staticConstructor = "of")
private static class AffordanceKey {
Class<?> type;
Method method;
String href;
}
}

View File

@@ -105,7 +105,7 @@ public class ControllerLinkBuilderFactory implements MethodLinkBuilderFactory<Co
return WebHandler.linkTo(invocationValue, ControllerLinkBuilder::new, (builder, invocation) -> {
MethodParameters parameters = new MethodParameters(invocation.getMethod());
MethodParameters parameters = MethodParameters.of(invocation.getMethod());
Iterator<Object> parameterValues = Arrays.asList(invocation.getArguments()).iterator();
for (MethodParameter parameter : parameters.getParameters()) {

View File

@@ -108,7 +108,7 @@ public class WebMvcLinkBuilderFactory implements MethodLinkBuilderFactory<WebMvc
return WebHandler.linkTo(invocationValue, WebMvcLinkBuilder::new, (builder, invocation) -> {
MethodParameters parameters = new MethodParameters(invocation.getMethod());
MethodParameters parameters = MethodParameters.of(invocation.getMethod());
Iterator<Object> parameterValues = Arrays.asList(invocation.getArguments()).iterator();
for (MethodParameter parameter : parameters.getParameters()) {

View File

@@ -55,7 +55,7 @@ public class UriTemplateUnitTest {
@Test
public void discoversRequestParam() {
UriTemplate template = new UriTemplate("/foo{?bar}");
UriTemplate template = UriTemplate.of("/foo{?bar}");
assertVariables(template, new TemplateVariable("bar", VariableType.REQUEST_PARAM));
}
@@ -66,7 +66,7 @@ public class UriTemplateUnitTest {
@Test
public void discoversRequestParamCntinued() {
UriTemplate template = new UriTemplate("/foo?bar{&foobar}");
UriTemplate template = UriTemplate.of("/foo?bar{&foobar}");
assertVariables(template, new TemplateVariable("foobar", VariableType.REQUEST_PARAM_CONTINUED));
}
@@ -77,7 +77,7 @@ public class UriTemplateUnitTest {
@Test
public void discoversOptionalPathVariable() {
UriTemplate template = new UriTemplate("/foo{/bar}");
UriTemplate template = UriTemplate.of("/foo{/bar}");
assertVariables(template, new TemplateVariable("bar", VariableType.SEGMENT));
}
@@ -88,7 +88,7 @@ public class UriTemplateUnitTest {
@Test
public void discoversPathVariable() {
UriTemplate template = new UriTemplate("/foo/{bar}");
UriTemplate template = UriTemplate.of("/foo/{bar}");
assertVariables(template, new TemplateVariable("bar", VariableType.PATH_VARIABLE));
}
@@ -99,7 +99,7 @@ public class UriTemplateUnitTest {
@Test
public void discoversFragment() {
UriTemplate template = new UriTemplate("/foo{#bar}");
UriTemplate template = UriTemplate.of("/foo{#bar}");
assertVariables(template, new TemplateVariable("bar", VariableType.FRAGMENT));
}
@@ -110,7 +110,7 @@ public class UriTemplateUnitTest {
@Test
public void discoversMultipleRequestParam() {
UriTemplate template = new UriTemplate("/foo{?bar,foobar}");
UriTemplate template = UriTemplate.of("/foo{?bar,foobar}");
assertVariables(template, new TemplateVariable("bar", VariableType.REQUEST_PARAM),
new TemplateVariable("foobar", VariableType.REQUEST_PARAM));
@@ -122,7 +122,7 @@ public class UriTemplateUnitTest {
@Test
public void expandsRequestParameter() {
UriTemplate template = new UriTemplate("/foo{?bar}");
UriTemplate template = UriTemplate.of("/foo{?bar}");
URI uri = template.expand(Collections.singletonMap("bar", "myBar"));
assertThat(uri.toString()).isEqualTo("/foo?bar=myBar");
@@ -138,7 +138,7 @@ public class UriTemplateUnitTest {
parameters.put("bar", "myBar");
parameters.put("fooBar", "myFooBar");
UriTemplate template = new UriTemplate("/foo{?bar,fooBar}");
UriTemplate template = UriTemplate.of("/foo{?bar,fooBar}");
URI uri = template.expand(parameters);
assertThat(uri.toString()).isEqualTo("/foo?bar=myBar&fooBar=myFooBar");
@@ -150,7 +150,7 @@ public class UriTemplateUnitTest {
@Test(expected = IllegalArgumentException.class)
public void rejectsMissingRequiredPathVariable() {
UriTemplate template = new UriTemplate("/foo/{bar}");
UriTemplate template = UriTemplate.of("/foo/{bar}");
template.expand(Collections.emptyMap());
}
@@ -160,7 +160,7 @@ public class UriTemplateUnitTest {
@Test
public void expandsMultipleVariablesViaArray() {
UriTemplate template = new UriTemplate("/foo{/bar}{?firstname,lastname}{#anchor}");
UriTemplate template = UriTemplate.of("/foo{/bar}{?firstname,lastname}{#anchor}");
URI uri = template.expand("path", "Dave", "Matthews", "discography");
assertThat(uri.toString()).isEqualTo("/foo/path?firstname=Dave&lastname=Matthews#discography");
}
@@ -170,7 +170,7 @@ public class UriTemplateUnitTest {
*/
@Test
public void expandsTemplateWithoutVariablesCorrectly() {
assertThat(new UriTemplate("/foo").expand().toString()).isEqualTo("/foo");
assertThat(UriTemplate.of("/foo").expand().toString()).isEqualTo("/foo");
}
/**
@@ -178,7 +178,7 @@ public class UriTemplateUnitTest {
*/
@Test
public void correctlyExpandsFullUri() {
assertThat(new UriTemplate("http://localhost:8080/foo{?bar}").expand().toString())
assertThat(UriTemplate.of("http://localhost:8080/foo{?bar}").expand().toString())
.isEqualTo("http://localhost:8080/foo");
}
@@ -188,7 +188,7 @@ public class UriTemplateUnitTest {
@Test
public void rendersUriTempalteWithPathVariable() {
UriTemplate template = new UriTemplate("/{foo}/bar{?page}");
UriTemplate template = UriTemplate.of("/{foo}/bar{?page}");
assertThat(template.toString()).isEqualTo("/{foo}/bar{?page}");
}
@@ -198,7 +198,7 @@ public class UriTemplateUnitTest {
@Test
public void addsTemplateVariables() {
UriTemplate source = new UriTemplate("/{foo}/bar{?page}");
UriTemplate source = UriTemplate.of("/{foo}/bar{?page}");
List<TemplateVariable> toAdd = Arrays.asList(new TemplateVariable("bar", VariableType.REQUEST_PARAM));
List<TemplateVariable> expected = new ArrayList<>();
@@ -214,7 +214,7 @@ public class UriTemplateUnitTest {
@Test
public void doesNotAddVariablesForAlreadyExistingRequestParameters() {
UriTemplate template = new UriTemplate("/?page=2");
UriTemplate template = UriTemplate.of("/?page=2");
UriTemplate result = template.with(new TemplateVariables(new TemplateVariable("page", VariableType.REQUEST_PARAM)));
assertThat(result.getVariableNames()).isEmpty();
@@ -228,7 +228,7 @@ public class UriTemplateUnitTest {
@Test
public void doesNotAddVariablesForAlreadyExistingFragment() {
UriTemplate template = new UriTemplate("/#fragment");
UriTemplate template = UriTemplate.of("/#fragment");
UriTemplate result = template.with(new TemplateVariables(new TemplateVariable("fragment", VariableType.FRAGMENT)));
assertThat(result.getVariableNames()).isEmpty();
}
@@ -239,7 +239,7 @@ public class UriTemplateUnitTest {
@Test
public void expandASimplePathVariable() {
UriTemplate template = new UriTemplate("/foo/{id}");
UriTemplate template = UriTemplate.of("/foo/{id}");
assertThat(template.expand(2).toString()).isEqualTo("/foo/2");
}
@@ -257,7 +257,7 @@ public class UriTemplateUnitTest {
@Test
public void allowsAddingTemplateVariable() {
UriTemplate template = new UriTemplate("/").with("q", VariableType.REQUEST_PARAM);
UriTemplate template = UriTemplate.of("/").with("q", VariableType.REQUEST_PARAM);
assertThat(template.toString()).isEqualTo("/{?q}");
}
@@ -268,7 +268,7 @@ public class UriTemplateUnitTest {
@Test
public void compositveValuesAreRecognisedAsVariableType() {
UriTemplate template = new UriTemplate("/foo{&bar,foobar*}");
UriTemplate template = UriTemplate.of("/foo{&bar,foobar*}");
assertVariables(template, new TemplateVariable("bar", VariableType.REQUEST_PARAM_CONTINUED),
new TemplateVariable("foobar", VariableType.COMPOSITE_PARAM));
@@ -281,7 +281,7 @@ public class UriTemplateUnitTest {
@SuppressWarnings("serial")
public void expandsCompositeValueAsAssociativeArray() {
UriTemplate template = new UriTemplate("/foo{&bar,foobar*}");
UriTemplate template = UriTemplate.of("/foo{&bar,foobar*}");
String expandedTemplate = template.expand(new HashMap<String, Object>() {
{
@@ -305,7 +305,7 @@ public class UriTemplateUnitTest {
@SuppressWarnings("serial")
public void expandsCompositeValueAsList() {
UriTemplate template = new UriTemplate("/foo{&bar,foobar*}");
UriTemplate template = UriTemplate.of("/foo{&bar,foobar*}");
String expandedTemplate = template.expand(new HashMap<String, Object>() {
{
@@ -324,7 +324,7 @@ public class UriTemplateUnitTest {
@SuppressWarnings("serial")
public void handlesCompositeValueAsSingleValue() {
UriTemplate template = new UriTemplate("/foo{&bar,foobar*}");
UriTemplate template = UriTemplate.of("/foo{&bar,foobar*}");
String expandedTemplate = template.expand(new HashMap<String, Object>() {
{

View File

@@ -40,7 +40,7 @@ import org.springframework.web.context.request.ServletRequestAttributes;
*/
public class DefaultCurieProviderUnitTest {
private static final UriTemplate URI_TEMPLATE = new UriTemplate("http://localhost:8080/rels/{rel}");
private static final UriTemplate URI_TEMPLATE = UriTemplate.of("http://localhost:8080/rels/{rel}");
CurieProvider provider = new DefaultCurieProvider("acme", URI_TEMPLATE);
@@ -63,12 +63,12 @@ public class DefaultCurieProviderUnitTest {
@Test(expected = IllegalArgumentException.class)
public void preventsUriTemplateWithoutVariable() {
new DefaultCurieProvider("acme", new UriTemplate("http://localhost:8080/rels"));
new DefaultCurieProvider("acme", UriTemplate.of("http://localhost:8080/rels"));
}
@Test(expected = IllegalArgumentException.class)
public void preventsUriTemplateWithMoreThanOneVariable() {
new DefaultCurieProvider("acme", new UriTemplate("http://localhost:8080/rels/{rel}/{another}"));
new DefaultCurieProvider("acme", UriTemplate.of("http://localhost:8080/rels/{rel}/{another}"));
}
@Test
@@ -170,7 +170,7 @@ public class DefaultCurieProviderUnitTest {
@Test
public void expandsNonAbsoluteUriWithApplicationUri() {
DefaultCurieProvider provider = new DefaultCurieProvider("name", new UriTemplate("/docs/{rel}"));
DefaultCurieProvider provider = new DefaultCurieProvider("name", UriTemplate.of("/docs/{rel}"));
MockHttpServletRequest request = new MockHttpServletRequest();
ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request);
@@ -189,8 +189,8 @@ public class DefaultCurieProviderUnitTest {
private static Map<String, UriTemplate> getCuries() {
Map<String, UriTemplate> curies = new HashMap<>(2);
curies.put("foo", new UriTemplate("/foo/{rel}"));
curies.put("bar", new UriTemplate("/bar/{rel}"));
curies.put("foo", UriTemplate.of("/foo/{rel}"));
curies.put("bar", UriTemplate.of("/bar/{rel}"));
return curies;
}

View File

@@ -45,7 +45,7 @@ public class HalEmbeddedBuilderUnitTest {
@Before
public void setUp() {
provider = new EvoInflectorLinkRelationProvider();
curieProvider = new DefaultCurieProvider("curie", new UriTemplate("http://localhost/{rel}"));
curieProvider = new DefaultCurieProvider("curie", UriTemplate.of("http://localhost/{rel}"));
}
@Test

View File

@@ -403,7 +403,7 @@ public class Jackson2HalIntegrationTest extends AbstractJackson2MarshallingInteg
CollectionModel<Object> resources = new CollectionModel<>(Collections.emptySet());
resources.add(new Link("foo", "myrel"));
CurieProvider provider = new DefaultCurieProvider("default", new UriTemplate("/doc{?rel}")) {
CurieProvider provider = new DefaultCurieProvider("default", UriTemplate.of("/doc{?rel}")) {
@Override
public Collection<? extends Object> getCurieInformation(Links links) {
return Arrays.asList(new Curie("foo", "bar"), new Curie("bar", "foo"));
@@ -538,7 +538,7 @@ public class Jackson2HalIntegrationTest extends AbstractJackson2MarshallingInteg
}
private ObjectMapper getCuriedObjectMapper() {
return getCuriedObjectMapper(new DefaultCurieProvider("foo", new UriTemplate("http://localhost:8080/rels/{rel}")));
return getCuriedObjectMapper(new DefaultCurieProvider("foo", UriTemplate.of("http://localhost:8080/rels/{rel}")));
}
private ObjectMapper getCuriedObjectMapper(CurieProvider provider) {

View File

@@ -338,7 +338,7 @@ public class Jackson2HalFormsIntegrationTest extends AbstractJackson2Marshalling
CollectionModel<Object> resources = new CollectionModel<>(Collections.emptySet());
resources.add(new Link("foo", "myrel"));
CurieProvider provider = new DefaultCurieProvider("default", new UriTemplate("/doc{?rel}")) {
CurieProvider provider = new DefaultCurieProvider("default", UriTemplate.of("/doc{?rel}")) {
@Override
public Collection<?> getCurieInformation(Links links) {
return Arrays.asList(new Curie("foo", "bar"), new Curie("bar", "foo"));
@@ -438,7 +438,7 @@ public class Jackson2HalFormsIntegrationTest extends AbstractJackson2Marshalling
private ObjectMapper getCuriedObjectMapper() {
return getCuriedObjectMapper(new DefaultCurieProvider("foo", new UriTemplate("http://localhost:8080/rels/{rel}")),
return getCuriedObjectMapper(new DefaultCurieProvider("foo", UriTemplate.of("http://localhost:8080/rels/{rel}")),
messageSource);
}

View File

@@ -26,7 +26,7 @@ import org.springframework.core.MethodParameter;
/**
* Unit tests for {@link MethodParameters}.
*
*
* @author Oliver Gierke
*/
public class MethodParametersUnitTest {
@@ -49,7 +49,7 @@ public class MethodParametersUnitTest {
public void returnsParametersOfAGivenType() throws Exception {
Method method = Sample.class.getMethod("method", String.class, String.class, Object.class);
MethodParameters methodParameters = new MethodParameters(method);
MethodParameters methodParameters = MethodParameters.of(method);
List<MethodParameter> objectParameters = methodParameters.getParametersOfType(Object.class);
assertThat(objectParameters).hasSize(1);

View File

@@ -74,12 +74,12 @@ public class PropertyUtilsTest {
public void resourceWrappedSpringMvcParameter() {
Method method = ReflectionUtils.findMethod(TestController.class, "newEmployee", EntityModel.class);
MethodParameters parameters = new MethodParameters(method);
MethodParameters parameters = MethodParameters.of(method);
ResolvableType resolvableType = parameters.getParametersWith(RequestBody.class).stream()
.findFirst()
.map(methodParameter -> ResolvableType.forMethodParameter(methodParameter.getMethod(), methodParameter.getParameterIndex()))
.orElseThrow(() -> new RuntimeException("Didn't find a parameter annotated with @RequestBody!"));
ResolvableType resolvableType = parameters.getParametersWith(RequestBody.class).stream().findFirst()
.map(methodParameter -> ResolvableType.forMethodParameter(methodParameter.getMethod(),
methodParameter.getParameterIndex()))
.orElseThrow(() -> new RuntimeException("Didn't find a parameter annotated with @RequestBody!"));
List<String> propertyNames = PropertyUtils.findPropertyNames(resolvableType);
@@ -90,19 +90,18 @@ public class PropertyUtilsTest {
@Test
public void objectWithIgnorableAttributes() {
EmployeeWithCustomizedReaders employee = new EmployeeWithCustomizedReaders("Frodo", "Baggins", "ring bearer", "password", "fbaggins", "ignore this one");
EmployeeWithCustomizedReaders employee = new EmployeeWithCustomizedReaders("Frodo", "Baggins", "ring bearer",
"password", "fbaggins", "ignore this one");
Map<String, Object> properties = PropertyUtils.findProperties(employee);
assertThat(properties).hasSize(6);
assertThat(properties.keySet()).containsExactlyInAnyOrder("firstName", "lastName", "role", "username", "fullName", "usernameAndLastName");
assertThat(properties.entrySet()).containsExactlyInAnyOrder(
new SimpleEntry<>("firstName", "Frodo"),
new SimpleEntry<>("lastName", "Baggins"),
new SimpleEntry<>("role", "ring bearer"),
new SimpleEntry<>("username", "fbaggins"),
new SimpleEntry<>("fullName", "Frodo Baggins"),
new SimpleEntry<>("usernameAndLastName", "fbaggins+++Baggins"));
assertThat(properties.keySet()).containsExactlyInAnyOrder("firstName", "lastName", "role", "username", "fullName",
"usernameAndLastName");
assertThat(properties.entrySet()).containsExactlyInAnyOrder(new SimpleEntry<>("firstName", "Frodo"),
new SimpleEntry<>("lastName", "Baggins"), new SimpleEntry<>("role", "ring bearer"),
new SimpleEntry<>("username", "fbaggins"), new SimpleEntry<>("fullName", "Frodo Baggins"),
new SimpleEntry<>("usernameAndLastName", "fbaggins+++Baggins"));
}
@Test
@@ -114,14 +113,13 @@ public class PropertyUtilsTest {
assertThat(properties).hasSize(2);
assertThat(properties.keySet()).containsExactlyInAnyOrder("name", "father");
assertThat(properties.entrySet()).containsExactlyInAnyOrder(
new SimpleEntry<>("name", "Frodo"),
new SimpleEntry<>("father", null));
assertThat(properties.entrySet()).containsExactlyInAnyOrder(new SimpleEntry<>("name", "Frodo"),
new SimpleEntry<>("father", null));
}
@Data
@AllArgsConstructor
@JsonIgnoreProperties({"ignoreThisProperty"})
@JsonIgnoreProperties({ "ignoreThisProperty" })
static class EmployeeWithCustomizedReaders {
private String firstName;
@@ -159,12 +157,11 @@ public class PropertyUtilsTest {
@RestController
static class TestController {
@GetMapping("/")
public Employee newEmployee(@RequestBody EntityModel<Employee> employee) {
return employee.getContent();
}
}
}