From 001f2d6c6961444f9bbbef1beabdc286f7441e11 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Fri, 10 Oct 2014 11:38:51 -0700 Subject: [PATCH] Improve performance of MessageSource condition Update the MessageSourceAutoConfiguration condition to skip scanning for well known JARs. Results are now also cached. Fixes gh-1689 --- .../MessageSourceAutoConfiguration.java | 74 ++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/MessageSourceAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/MessageSourceAutoConfiguration.java index 6163d590e4e..301acdedd89 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/MessageSourceAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/MessageSourceAutoConfiguration.java @@ -17,6 +17,8 @@ package org.springframework.boot.autoconfigure; import java.io.IOException; +import java.util.Iterator; +import java.util.Set; import org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration.ResourceBundleCondition; import org.springframework.boot.autoconfigure.condition.ConditionOutcome; @@ -35,6 +37,7 @@ import org.springframework.core.annotation.Order; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.type.AnnotatedTypeMetadata; +import org.springframework.util.ConcurrentReferenceHashMap; import org.springframework.util.StringUtils; import static org.springframework.util.StringUtils.commaDelimitedListToStringArray; @@ -44,6 +47,7 @@ import static org.springframework.util.StringUtils.trimAllWhitespace; * {@link EnableAutoConfiguration Auto-configuration} for {@link MessageSource}. * * @author Dave Syer + * @author Phillip Webb */ @Configuration @ConditionalOnMissingBean(MessageSource.class) @@ -99,11 +103,23 @@ public class MessageSourceAutoConfiguration { protected static class ResourceBundleCondition extends SpringBootCondition { + private static ConcurrentReferenceHashMap cache = new ConcurrentReferenceHashMap(); + @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { String basename = context.getEnvironment().getProperty( "spring.messages.basename", "messages"); + ConditionOutcome outcome = cache.get(basename); + if (outcome == null) { + outcome = getMatchOutcomeForBasename(context, basename); + cache.put(basename, outcome); + } + return outcome; + } + + private ConditionOutcome getMatchOutcomeForBasename(ConditionContext context, + String basename) { for (String name : commaDelimitedListToStringArray(trimAllWhitespace(basename))) { for (Resource resource : getResources(context.getClassLoader(), name)) { if (resource.exists()) { @@ -118,7 +134,7 @@ public class MessageSourceAutoConfiguration { private Resource[] getResources(ClassLoader classLoader, String name) { try { - return new PathMatchingResourcePatternResolver(classLoader) + return new SkipPatternPathMatchingResourcePatternResolver(classLoader) .getResources("classpath*:" + name + "*.properties"); } catch (IOException ex) { @@ -128,4 +144,60 @@ public class MessageSourceAutoConfiguration { } + /** + * {@link PathMatchingResourcePatternResolver} that skips well known JARs that don't + * contain messages.properties. + */ + private static class SkipPatternPathMatchingResourcePatternResolver extends + PathMatchingResourcePatternResolver { + + private static final ClassLoader ROOT_CLASSLOADER; + static { + ClassLoader classLoader = ClassLoader.getSystemClassLoader(); + while (classLoader.getParent() != null) { + classLoader = classLoader.getParent(); + } + ROOT_CLASSLOADER = classLoader; + } + + private static final String[] SKIPPED = { "aspectjweaver-", "hibernate-core-", + "hsqldb-", "jackson-annotations-", "jackson-core-", "jackson-databind-", + "javassist-", "snakeyaml-", "spring-aop-", "spring-beans-", + "spring-boot-", "spring-boot-actuator-", "spring-boot-autoconfigure-", + "spring-core-", "spring-context-", "spring-data-commons-", + "spring-expression-", "spring-jdbc-", "spring-orm-", "spring-tx-", + "spring-web-", "spring-webmvc-", "tomcat-embed-", "joda-time-", + "hibernate-entitymanager-", "hibernate-validator-", "logback-classic-", + "logback-core-", "thymeleaf-" }; + + public SkipPatternPathMatchingResourcePatternResolver(ClassLoader classLoader) { + super(classLoader); + } + + @Override + protected void addAllClassLoaderJarRoots(ClassLoader classLoader, + Set result) { + if (classLoader != ROOT_CLASSLOADER) { + super.addAllClassLoaderJarRoots(classLoader, result); + } + }; + + @Override + protected Set doFindAllClassPathResources(String path) + throws IOException { + Set resources = super.doFindAllClassPathResources(path); + for (Iterator iterator = resources.iterator(); iterator.hasNext();) { + Resource resource = iterator.next(); + for (String skipped : SKIPPED) { + if (resource.getFilename().startsWith(skipped)) { + iterator.remove(); + break; + } + } + } + return resources; + } + + } + }