From 787d8f5916652dc8c746a50c89f0be3caac1d158 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 10 Sep 2012 22:35:27 +0200 Subject: [PATCH] SpringFactoriesLoader as the simplest possible mechanism for BeanInfoFactory loading --- .../beans/CachedIntrospectionResults.java | 22 +-- .../io/support/SpringFactoriesLoader.java | 133 ++++-------------- .../support/SpringFactoriesLoaderTests.java | 47 ------- 3 files changed, 31 insertions(+), 171 deletions(-) delete mode 100644 spring-core/src/test/java/org/springframework/core/io/support/SpringFactoriesLoaderTests.java diff --git a/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java b/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java index 8da24fc8028..92bca358b86 100644 --- a/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java +++ b/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java @@ -62,6 +62,10 @@ public class CachedIntrospectionResults { private static final Log logger = LogFactory.getLog(CachedIntrospectionResults.class); + /** Stores the BeanInfoFactory instances */ + private static List beanInfoFactories = + SpringFactoriesLoader.loadFactories(BeanInfoFactory.class, CachedIntrospectionResults.class.getClassLoader()); + /** * Set of ClassLoaders that this CachedIntrospectionResults class will always * accept classes from, even if the classes do not qualify as cache-safe. @@ -75,11 +79,6 @@ public class CachedIntrospectionResults { */ static final Map classCache = Collections.synchronizedMap(new WeakHashMap()); - /** Stores the BeanInfoFactory instances */ - private static List beanInfoFactories; - - private static final Object beanInfoFactoriesMutex = new Object(); - /** * Accept the given ClassLoader as cache-safe, even if its classes would @@ -230,7 +229,6 @@ public class CachedIntrospectionResults { } BeanInfo beanInfo = null; - List beanInfoFactories = getBeanInfoFactories(beanClass.getClassLoader()); for (BeanInfoFactory beanInfoFactory : beanInfoFactories) { beanInfo = beanInfoFactory.getBeanInfo(beanClass); if (beanInfo != null) { @@ -325,16 +323,4 @@ public class CachedIntrospectionResults { } } - private static List getBeanInfoFactories(ClassLoader classLoader) { - if (beanInfoFactories == null) { - synchronized (beanInfoFactoriesMutex) { - if (beanInfoFactories == null) { - beanInfoFactories = Collections.synchronizedList(SpringFactoriesLoader - .loadFactories(BeanInfoFactory.class, classLoader)); - } - } - } - return beanInfoFactories; - } - } diff --git a/spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java b/spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java index 6a67ec7e3a7..13ae7ca91f4 100644 --- a/spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java +++ b/spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java @@ -20,7 +20,6 @@ import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.Enumeration; import java.util.List; import java.util.Properties; @@ -28,22 +27,19 @@ import java.util.Properties; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.core.annotation.AnnotationAwareOrderComparator; +import org.springframework.core.OrderComparator; import org.springframework.core.io.UrlResource; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; /** - * General purpose factory loading mechanism. + * General purpose factory loading mechanism for internal use within the framework. * *

The {@code SpringFactoriesLoader} loads and instantiates factories of a given type - * from a given file location. If a location is not given, the {@linkplain - * #DEFAULT_FACTORIES_LOCATION default location} is used. - * - *

The file should be in {@link Properties} format, where the key is the fully - * qualified interface or abstract class name, and the value is a comma separated list of - * implementations. For instance: + * from "META-INF/spring.factories" files. The file should be in {@link Properties} format, + * where the key is the fully qualified interface or abstract class name, and the value + * is a comma-separated list of implementation class names. For instance: * *

example.MyService=example.MyServiceImpl1,example.MyServiceImpl2
* @@ -51,148 +47,73 @@ import org.springframework.util.StringUtils; * {@code MyServiceImpl2} are the two implementations. * * @author Arjen Poutsma + * @author Juergen Hoeller * @since 3.2 */ public abstract class SpringFactoriesLoader { + /** The location to look for the factories. Can be present in multiple JAR files. */ + private static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; + private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class); - /** The location to look for the factories. Can be present in multiple JAR files. */ - public static final String DEFAULT_FACTORIES_LOCATION = "META-INF/spring.factories"; - - /** - * Loads the factory implementations of the given type from the default location. - * - *

The returned factories are ordered in accordance with the {@link - * AnnotationAwareOrderComparator}. - * - * @param factoryClass the interface or abstract class representing the factory - * @throws IllegalArgumentException in case of errors - */ - public static List loadFactories(Class factoryClass) { - return loadFactories(factoryClass, null, null); - } /** * Loads the factory implementations of the given type from the default location, using * the given class loader. - * - *

The returned factories are ordered in accordance with the {@link - * AnnotationAwareOrderComparator}. - * + *

The returned factories are ordered in accordance with the {@link OrderComparator}. * @param factoryClass the interface or abstract class representing the factory - * @param classLoader the ClassLoader to use for loading, can be {@code null} to use the - * default - * @throws IllegalArgumentException in case of errors + * @param classLoader the ClassLoader to use for loading (can be {@code null} to use the default) */ - public static List loadFactories(Class factoryClass, - ClassLoader classLoader) { - return loadFactories(factoryClass, classLoader, null); - } - - /** - * Loads the factory implementations of the given type from the given location, using the - * given class loader. - * - *

The returned factories are ordered in accordance with the {@link - * AnnotationAwareOrderComparator}. - * - * @param factoryClass the interface or abstract class representing the factory - * @param classLoader the ClassLoader to use for loading, can be {@code null} to use the - * default - * @param factoriesLocation the factories file location, can be {@code null} to use the - * {@linkplain #DEFAULT_FACTORIES_LOCATION default} - * @throws IllegalArgumentException in case of errors - */ - public static List loadFactories(Class factoryClass, - ClassLoader classLoader, String factoriesLocation) { + public static List loadFactories(Class factoryClass, ClassLoader classLoader) { Assert.notNull(factoryClass, "'factoryClass' must not be null"); - if (classLoader == null) { - classLoader = ClassUtils.getDefaultClassLoader(); + classLoader = SpringFactoriesLoader.class.getClassLoader(); } - if (factoriesLocation == null) { - factoriesLocation = DEFAULT_FACTORIES_LOCATION; - } - - List factoryNames = - loadFactoryNames(factoryClass, classLoader, factoriesLocation); - + List factoryNames = loadFactoryNames(factoryClass, classLoader); if (logger.isTraceEnabled()) { - logger.trace( - "Loaded [" + factoryClass.getName() + "] names: " + factoryNames); + logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames); } - List result = new ArrayList(factoryNames.size()); for (String factoryName : factoryNames) { result.add(instantiateFactory(factoryName, factoryClass, classLoader)); } - - Collections.sort(result, new AnnotationAwareOrderComparator()); - + OrderComparator.sort(result); return result; } - private static List loadFactoryNames(Class factoryClass, - ClassLoader classLoader, String factoriesLocation) { - + private static List loadFactoryNames(Class factoryClass, ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); - try { List result = new ArrayList(); - Enumeration urls = classLoader.getResources(factoriesLocation); + Enumeration urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION); while (urls.hasMoreElements()) { URL url = urls.nextElement(); - Properties properties = - PropertiesLoaderUtils.loadProperties(new UrlResource(url)); + Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url)); String factoryClassNames = properties.getProperty(factoryClassName); - result.addAll(Arrays.asList( - StringUtils.commaDelimitedListToStringArray(factoryClassNames))); + result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames))); } return result; } catch (IOException ex) { - throw new IllegalArgumentException( - "Unable to load [" + factoryClass.getName() + - "] factories from location [" + - factoriesLocation + "]", ex); + throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + + "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } - - } @SuppressWarnings("unchecked") - private static T instantiateFactory(String instanceClassName, - Class factoryClass, ClassLoader classLoader) { + private static T instantiateFactory(String instanceClassName, Class factoryClass, ClassLoader classLoader) { try { Class instanceClass = ClassUtils.forName(instanceClassName, classLoader); if (!factoryClass.isAssignableFrom(instanceClass)) { throw new IllegalArgumentException( - "Class [" + instanceClassName + "] is not assignable to [" + - factoryClass.getName() + "]"); + "Class [" + instanceClassName + "] is not assignable to [" + factoryClass.getName() + "]"); } return (T) instanceClass.newInstance(); } - catch (ClassNotFoundException ex) { - throw new IllegalArgumentException( - factoryClass.getName() + " class [" + instanceClassName + - "] not found", ex); - } - catch (LinkageError err) { - throw new IllegalArgumentException( - "Invalid " + factoryClass.getName() + " class [" + instanceClassName + - "]: problem with handler class file or dependent class", err); - } - catch (InstantiationException ex) { - throw new IllegalArgumentException( - "Could not instantiate bean class [" + instanceClassName + - "]: Is it an abstract class?", ex); - } - catch (IllegalAccessException ex) { - throw new IllegalArgumentException( - "Could not instantiate bean class [" + instanceClassName + - "Is the constructor accessible?", ex); + catch (Throwable ex) { + throw new IllegalArgumentException("Cannot instantiate factory class: " + factoryClass.getName(), ex); } } -} \ No newline at end of file +} diff --git a/spring-core/src/test/java/org/springframework/core/io/support/SpringFactoriesLoaderTests.java b/spring-core/src/test/java/org/springframework/core/io/support/SpringFactoriesLoaderTests.java deleted file mode 100644 index 98510eb960e..00000000000 --- a/spring-core/src/test/java/org/springframework/core/io/support/SpringFactoriesLoaderTests.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2002-2012 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.core.io.support; - -import java.util.List; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import org.junit.Test; - -/** @author Arjen Poutsma */ -public class SpringFactoriesLoaderTests { - - private static final String FACTORIES_LOCATION = - "org/springframework/core/io/support/springFactoriesLoaderTests.properties"; - - @Test - public void loadFactories() { - List factories = SpringFactoriesLoader - .loadFactories(DummyFactory.class, null, FACTORIES_LOCATION); - - assertEquals(2, factories.size()); - assertTrue(factories.get(0) instanceof MyDummyFactory1); - assertTrue(factories.get(1) instanceof MyDummyFactory2); - } - - @Test(expected = IllegalArgumentException.class) - public void loadInvalid() { - SpringFactoriesLoader.loadFactories(String.class, null, - FACTORIES_LOCATION); - } - -}