From a5f2b602fbde718a3f0604495146d0009823bf17 Mon Sep 17 00:00:00 2001 From: Michal Stochmialek Date: Mon, 12 Nov 2018 09:52:18 +0100 Subject: [PATCH] #749 - More exhaustive caching of mappings, UriTemplates and base URIs. We now cache the UriTemplates created for request mappings to avoid repeated parsing of the input string. We also cache the base URI within the RequestAttributes for a particular request. --- .../core/CachingMappingDiscoverer.java | 73 +++++++++++++++++++ .../hateoas/mvc/ControllerLinkBuilder.java | 42 ++++++++--- .../mvc/ControllerLinkBuilderFactory.java | 15 ++-- .../hateoas/mvc/UriTemplateFactory.java | 22 ++++++ 4 files changed, 137 insertions(+), 15 deletions(-) create mode 100644 src/main/java/org/springframework/hateoas/core/CachingMappingDiscoverer.java create mode 100644 src/main/java/org/springframework/hateoas/mvc/UriTemplateFactory.java diff --git a/src/main/java/org/springframework/hateoas/core/CachingMappingDiscoverer.java b/src/main/java/org/springframework/hateoas/core/CachingMappingDiscoverer.java new file mode 100644 index 00000000..39d64ea0 --- /dev/null +++ b/src/main/java/org/springframework/hateoas/core/CachingMappingDiscoverer.java @@ -0,0 +1,73 @@ +package org.springframework.hateoas.core; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class CachingMappingDiscoverer implements MappingDiscoverer { + private Map mappingCache = new ConcurrentHashMap(); + private MappingDiscoverer discoverer; + + public CachingMappingDiscoverer(MappingDiscoverer discoverer) { + this.discoverer = discoverer; + } + + @Override + public String getMapping(final Class type) { + String key = key(type, null); + return getMapping(key, new CachedCall() { + @Override + public String getMapping() { + return discoverer.getMapping(type); + } + }); + } + + @Override + public String getMapping(final Method method) { + String key = key(method.getDeclaringClass(), method); + return getMapping(key, new CachedCall() { + @Override + public String getMapping() { + return discoverer.getMapping(method); + } + }); + } + + @Override + public String getMapping(final Class type, final Method method) { + String key = key(type, method); + return getMapping(key, new CachedCall() { + @Override + public String getMapping() { + return discoverer.getMapping(type, method); + } + }); + } + + public String getMapping(String key, CachedCall cachedCall) { + if (mappingCache.containsKey(key)) { + return mappingCache.get(key); + } else { + String mapping = cachedCall.getMapping(); + mappingCache.put(key, mapping); + return mapping; + } + } + + private interface CachedCall { + String getMapping(); + } + + private String key(Class type, Method method) { + StringBuilder buf = new StringBuilder(); + buf.append(type.getName()); + if (method != null) { + buf.append(method.getName()); + for (Class par: method.getParameterTypes()) { + buf.append(par.getName()); + } + } + return buf.toString(); + } +} diff --git a/src/main/java/org/springframework/hateoas/mvc/ControllerLinkBuilder.java b/src/main/java/org/springframework/hateoas/mvc/ControllerLinkBuilder.java index 1aaca6fb..bc20f56a 100755 --- a/src/main/java/org/springframework/hateoas/mvc/ControllerLinkBuilder.java +++ b/src/main/java/org/springframework/hateoas/mvc/ControllerLinkBuilder.java @@ -62,6 +62,7 @@ public class ControllerLinkBuilder extends LinkBuilderSupport { - private static final MappingDiscoverer DISCOVERER = new AnnotationMappingDiscoverer(RequestMapping.class); + private static final MappingDiscoverer DISCOVERER = new CachingMappingDiscoverer( + new AnnotationMappingDiscoverer(RequestMapping.class)); private static final AnnotatedParametersParameterAccessor PATH_VARIABLE_ACCESSOR = new AnnotatedParametersParameterAccessor( new AnnotationAttribute(PathVariable.class)); private static final AnnotatedParametersParameterAccessor REQUEST_PARAM_ACCESSOR = new RequestParamParameterAccessor(); private List uriComponentsContributors = new ArrayList(); + private UriTemplateFactory uriTemplateFactory = new UriTemplateFactory(); /** * Configures the {@link UriComponentsContributor} to be used when building {@link Link} instances from method @@ -103,7 +106,7 @@ public class ControllerLinkBuilderFactory implements MethodLinkBuilderFactory values = new HashMap(); Iterator names = template.getVariableNames().iterator(); @@ -280,7 +283,7 @@ public class ControllerLinkBuilderFactory implements MethodLinkBuilderFactory templateCache = new ConcurrentHashMap(); + UriTemplate templateFor(String mapping) { + if (templateCache.containsKey(mapping)) { + return templateCache.get(mapping); + } else { + UriTemplate template = new UriTemplate(mapping); + templateCache.put(mapping, template); + return template; + } + } +}