diff --git a/spring-core/src/main/java/org/springframework/core/SpringFactoriesLoader.java b/spring-core/src/main/java/org/springframework/core/SpringFactoriesLoader.java
new file mode 100644
index 0000000000..e6fc7e3a04
--- /dev/null
+++ b/spring-core/src/main/java/org/springframework/core/SpringFactoriesLoader.java
@@ -0,0 +1,186 @@
+/*
+ * 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;
+
+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;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.core.io.UrlResource;
+import org.springframework.core.io.support.PropertiesLoaderUtils;
+import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.StringUtils;
+
+/**
+ * General purpose factory loading mechanism.
+ *
+ *
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 keys is the fully
+ * qualified interface or abstract class name, and the value is a comma separated list of
+ * implementations. For instance:
+ *
+ * example.MyService=example.MyServiceImpl1,example.MyServiceImpl2
+ *
+ * where {@code MyService} is the name of the interface, and {@code MyServiceImpl1} and
+ * {@code MyServiceImpl2} are the two implementations.
+ *
+ * @author Arjen Poutsma
+ * @since 3.2
+ */
+public abstract class SpringFactoriesLoader {
+
+ 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, using
+ * the given class loader.
+ *
+ * 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
+ */
+ 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 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
+ * @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) {
+ Assert.notNull(factoryClass, "'factoryClass' must not be null");
+
+ if (classLoader == null) {
+ classLoader = ClassUtils.getDefaultClassLoader();
+ }
+ if (factoriesLocation == null) {
+ factoriesLocation = DEFAULT_FACTORIES_LOCATION;
+ }
+
+ List factoryNames =
+ loadFactoryNames(factoryClass, classLoader, factoriesLocation);
+
+ if (logger.isTraceEnabled()) {
+ 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 OrderComparator());
+
+ return result;
+ }
+
+ private static List loadFactoryNames(Class factoryClass,
+ ClassLoader classLoader,
+ String factoriesLocation) {
+
+ String factoryClassName = factoryClass.getName();
+
+ try {
+ List result = new ArrayList();
+ Enumeration urls = classLoader.getResources(factoriesLocation);
+ while (urls.hasMoreElements()) {
+ URL url = (URL) urls.nextElement();
+ Properties properties =
+ PropertiesLoaderUtils.loadProperties(new UrlResource(url));
+ String factoryClassNames = properties.getProperty(factoryClassName);
+ 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);
+ }
+
+
+ }
+
+ @SuppressWarnings("unchecked")
+ 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() + "]");
+ }
+ 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);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/spring-core/src/test/java/org/springframework/core/DummyFactory.java b/spring-core/src/test/java/org/springframework/core/DummyFactory.java
new file mode 100644
index 0000000000..d19257a79a
--- /dev/null
+++ b/spring-core/src/test/java/org/springframework/core/DummyFactory.java
@@ -0,0 +1,28 @@
+/*
+ * 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;
+
+/**
+ * Used by {@link SpringFactoriesLoaderTests}
+ *
+ * @author Arjen Poutsma
+ */
+public interface DummyFactory {
+
+ String getString();
+
+}
diff --git a/spring-core/src/test/java/org/springframework/core/MyDummyFactory1.java b/spring-core/src/test/java/org/springframework/core/MyDummyFactory1.java
new file mode 100644
index 0000000000..f8d2a122f0
--- /dev/null
+++ b/spring-core/src/test/java/org/springframework/core/MyDummyFactory1.java
@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+/**
+ * Used by {@link SpringFactoriesLoaderTests}
+ *
+ * @author Arjen Poutsma
+ */
+public class MyDummyFactory1 implements DummyFactory, Ordered {
+
+ public int getOrder() {
+ return 1;
+ }
+
+ public String getString() {
+ return "Foo";
+ }
+}
diff --git a/spring-core/src/test/java/org/springframework/core/MyDummyFactory2.java b/spring-core/src/test/java/org/springframework/core/MyDummyFactory2.java
new file mode 100644
index 0000000000..0a7d5f6245
--- /dev/null
+++ b/spring-core/src/test/java/org/springframework/core/MyDummyFactory2.java
@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+/**
+ * Used by {@link org.springframework.core.SpringFactoriesLoaderTests}
+ *
+ * @author Arjen Poutsma
+ */
+public class MyDummyFactory2 implements DummyFactory, Ordered {
+
+ public int getOrder() {
+ return 2;
+ }
+
+ public String getString() {
+ return "Bar";
+ }
+}
diff --git a/spring-core/src/test/java/org/springframework/core/SpringFactoriesLoaderTests.java b/spring-core/src/test/java/org/springframework/core/SpringFactoriesLoaderTests.java
new file mode 100644
index 0000000000..07fb50fbc6
--- /dev/null
+++ b/spring-core/src/test/java/org/springframework/core/SpringFactoriesLoaderTests.java
@@ -0,0 +1,45 @@
+/*
+ * 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;
+
+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 {
+
+ @Test
+ public void loadFactories() {
+ List factories = SpringFactoriesLoader
+ .loadFactories(DummyFactory.class, null,
+ "org/springframework/core/springFactoriesLoaderTests.properties");
+
+ 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,
+ "org/springframework/core/springFactoriesLoaderTests.properties");
+ }
+
+}
diff --git a/spring-core/src/test/resources/org/springframework/core/springFactoriesLoaderTests.properties b/spring-core/src/test/resources/org/springframework/core/springFactoriesLoaderTests.properties
new file mode 100644
index 0000000000..a2813e2969
--- /dev/null
+++ b/spring-core/src/test/resources/org/springframework/core/springFactoriesLoaderTests.properties
@@ -0,0 +1,2 @@
+org.springframework.core.DummyFactory=org.springframework.core.MyDummyFactory2,org.springframework.core.MyDummyFactory1
+java.lang.String=org.springframework.core.MyDummyFactory1
\ No newline at end of file