Improve performance of MessageSource condition

Update the MessageSourceAutoConfiguration condition to skip scanning for
well known JARs. Results are now also cached.

Fixes gh-1689
This commit is contained in:
Phillip Webb 2014-10-10 11:38:51 -07:00
parent 828efb2589
commit 001f2d6c69
1 changed files with 73 additions and 1 deletions

View File

@ -17,6 +17,8 @@
package org.springframework.boot.autoconfigure; package org.springframework.boot.autoconfigure;
import java.io.IOException; import java.io.IOException;
import java.util.Iterator;
import java.util.Set;
import org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration.ResourceBundleCondition; import org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration.ResourceBundleCondition;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; 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.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import static org.springframework.util.StringUtils.commaDelimitedListToStringArray; 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}. * {@link EnableAutoConfiguration Auto-configuration} for {@link MessageSource}.
* *
* @author Dave Syer * @author Dave Syer
* @author Phillip Webb
*/ */
@Configuration @Configuration
@ConditionalOnMissingBean(MessageSource.class) @ConditionalOnMissingBean(MessageSource.class)
@ -99,11 +103,23 @@ public class MessageSourceAutoConfiguration {
protected static class ResourceBundleCondition extends SpringBootCondition { protected static class ResourceBundleCondition extends SpringBootCondition {
private static ConcurrentReferenceHashMap<String, ConditionOutcome> cache = new ConcurrentReferenceHashMap<String, ConditionOutcome>();
@Override @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
String basename = context.getEnvironment().getProperty( String basename = context.getEnvironment().getProperty(
"spring.messages.basename", "messages"); "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 (String name : commaDelimitedListToStringArray(trimAllWhitespace(basename))) {
for (Resource resource : getResources(context.getClassLoader(), name)) { for (Resource resource : getResources(context.getClassLoader(), name)) {
if (resource.exists()) { if (resource.exists()) {
@ -118,7 +134,7 @@ public class MessageSourceAutoConfiguration {
private Resource[] getResources(ClassLoader classLoader, String name) { private Resource[] getResources(ClassLoader classLoader, String name) {
try { try {
return new PathMatchingResourcePatternResolver(classLoader) return new SkipPatternPathMatchingResourcePatternResolver(classLoader)
.getResources("classpath*:" + name + "*.properties"); .getResources("classpath*:" + name + "*.properties");
} }
catch (IOException ex) { 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<Resource> result) {
if (classLoader != ROOT_CLASSLOADER) {
super.addAllClassLoaderJarRoots(classLoader, result);
}
};
@Override
protected Set<Resource> doFindAllClassPathResources(String path)
throws IOException {
Set<Resource> resources = super.doFindAllClassPathResources(path);
for (Iterator<Resource> iterator = resources.iterator(); iterator.hasNext();) {
Resource resource = iterator.next();
for (String skipped : SKIPPED) {
if (resource.getFilename().startsWith(skipped)) {
iterator.remove();
break;
}
}
}
return resources;
}
}
} }