Skip to content

Commit 787d8f5

Browse files
jhoellerunknown
authored andcommitted
SpringFactoriesLoader as the simplest possible mechanism for BeanInfoFactory loading
1 parent 0a42c80 commit 787d8f5

File tree

3 files changed

+31
-171
lines changed

3 files changed

+31
-171
lines changed

spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ public class CachedIntrospectionResults {
6262

6363
private static final Log logger = LogFactory.getLog(CachedIntrospectionResults.class);
6464

65+
/** Stores the BeanInfoFactory instances */
66+
private static List<BeanInfoFactory> beanInfoFactories =
67+
SpringFactoriesLoader.loadFactories(BeanInfoFactory.class, CachedIntrospectionResults.class.getClassLoader());
68+
6569
/**
6670
* Set of ClassLoaders that this CachedIntrospectionResults class will always
6771
* accept classes from, even if the classes do not qualify as cache-safe.
@@ -75,11 +79,6 @@ public class CachedIntrospectionResults {
7579
*/
7680
static final Map<Class, Object> classCache = Collections.synchronizedMap(new WeakHashMap<Class, Object>());
7781

78-
/** Stores the BeanInfoFactory instances */
79-
private static List<BeanInfoFactory> beanInfoFactories;
80-
81-
private static final Object beanInfoFactoriesMutex = new Object();
82-
8382

8483
/**
8584
* Accept the given ClassLoader as cache-safe, even if its classes would
@@ -230,7 +229,6 @@ private CachedIntrospectionResults(Class beanClass, boolean cacheFullMetadata) t
230229
}
231230

232231
BeanInfo beanInfo = null;
233-
List<BeanInfoFactory> beanInfoFactories = getBeanInfoFactories(beanClass.getClassLoader());
234232
for (BeanInfoFactory beanInfoFactory : beanInfoFactories) {
235233
beanInfo = beanInfoFactory.getBeanInfo(beanClass);
236234
if (beanInfo != null) {
@@ -325,16 +323,4 @@ private PropertyDescriptor buildGenericTypeAwarePropertyDescriptor(Class beanCla
325323
}
326324
}
327325

328-
private static List<BeanInfoFactory> getBeanInfoFactories(ClassLoader classLoader) {
329-
if (beanInfoFactories == null) {
330-
synchronized (beanInfoFactoriesMutex) {
331-
if (beanInfoFactories == null) {
332-
beanInfoFactories = Collections.synchronizedList(SpringFactoriesLoader
333-
.loadFactories(BeanInfoFactory.class, classLoader));
334-
}
335-
}
336-
}
337-
return beanInfoFactories;
338-
}
339-
340326
}

spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java

Lines changed: 27 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -20,179 +20,100 @@
2020
import java.net.URL;
2121
import java.util.ArrayList;
2222
import java.util.Arrays;
23-
import java.util.Collections;
2423
import java.util.Enumeration;
2524
import java.util.List;
2625
import java.util.Properties;
2726

2827
import org.apache.commons.logging.Log;
2928
import org.apache.commons.logging.LogFactory;
3029

31-
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
30+
import org.springframework.core.OrderComparator;
3231
import org.springframework.core.io.UrlResource;
3332
import org.springframework.util.Assert;
3433
import org.springframework.util.ClassUtils;
3534
import org.springframework.util.StringUtils;
3635

3736
/**
38-
* General purpose factory loading mechanism.
37+
* General purpose factory loading mechanism for internal use within the framework.
3938
*
4039
* <p>The {@code SpringFactoriesLoader} loads and instantiates factories of a given type
41-
* from a given file location. If a location is not given, the {@linkplain
42-
* #DEFAULT_FACTORIES_LOCATION default location} is used.
43-
*
44-
* <p>The file should be in {@link Properties} format, where the key is the fully
45-
* qualified interface or abstract class name, and the value is a comma separated list of
46-
* implementations. For instance:
40+
* from "META-INF/spring.factories" files. The file should be in {@link Properties} format,
41+
* where the key is the fully qualified interface or abstract class name, and the value
42+
* is a comma-separated list of implementation class names. For instance:
4743
*
4844
* <pre class="code">example.MyService=example.MyServiceImpl1,example.MyServiceImpl2</pre>
4945
*
5046
* where {@code MyService} is the name of the interface, and {@code MyServiceImpl1} and
5147
* {@code MyServiceImpl2} are the two implementations.
5248
*
5349
* @author Arjen Poutsma
50+
* @author Juergen Hoeller
5451
* @since 3.2
5552
*/
5653
public abstract class SpringFactoriesLoader {
5754

58-
private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
59-
6055
/** The location to look for the factories. Can be present in multiple JAR files. */
61-
public static final String DEFAULT_FACTORIES_LOCATION = "META-INF/spring.factories";
56+
private static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
57+
58+
private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
6259

63-
/**
64-
* Loads the factory implementations of the given type from the default location.
65-
*
66-
* <p>The returned factories are ordered in accordance with the {@link
67-
* AnnotationAwareOrderComparator}.
68-
*
69-
* @param factoryClass the interface or abstract class representing the factory
70-
* @throws IllegalArgumentException in case of errors
71-
*/
72-
public static <T> List<T> loadFactories(Class<T> factoryClass) {
73-
return loadFactories(factoryClass, null, null);
74-
}
7560

7661
/**
7762
* Loads the factory implementations of the given type from the default location, using
7863
* the given class loader.
79-
*
80-
* <p>The returned factories are ordered in accordance with the {@link
81-
* AnnotationAwareOrderComparator}.
82-
*
83-
* @param factoryClass the interface or abstract class representing the factory
84-
* @param classLoader the ClassLoader to use for loading, can be {@code null} to use the
85-
* default
86-
* @throws IllegalArgumentException in case of errors
87-
*/
88-
public static <T> List<T> loadFactories(Class<T> factoryClass,
89-
ClassLoader classLoader) {
90-
return loadFactories(factoryClass, classLoader, null);
91-
}
92-
93-
/**
94-
* Loads the factory implementations of the given type from the given location, using the
95-
* given class loader.
96-
*
97-
* <p>The returned factories are ordered in accordance with the {@link
98-
* AnnotationAwareOrderComparator}.
99-
*
64+
* <p>The returned factories are ordered in accordance with the {@link OrderComparator}.
10065
* @param factoryClass the interface or abstract class representing the factory
101-
* @param classLoader the ClassLoader to use for loading, can be {@code null} to use the
102-
* default
103-
* @param factoriesLocation the factories file location, can be {@code null} to use the
104-
* {@linkplain #DEFAULT_FACTORIES_LOCATION default}
105-
* @throws IllegalArgumentException in case of errors
66+
* @param classLoader the ClassLoader to use for loading (can be {@code null} to use the default)
10667
*/
107-
public static <T> List<T> loadFactories(Class<T> factoryClass,
108-
ClassLoader classLoader, String factoriesLocation) {
68+
public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) {
10969
Assert.notNull(factoryClass, "'factoryClass' must not be null");
110-
11170
if (classLoader == null) {
112-
classLoader = ClassUtils.getDefaultClassLoader();
113-
}
114-
if (factoriesLocation == null) {
115-
factoriesLocation = DEFAULT_FACTORIES_LOCATION;
71+
classLoader = SpringFactoriesLoader.class.getClassLoader();
11672
}
117-
118-
List<String> factoryNames =
119-
loadFactoryNames(factoryClass, classLoader, factoriesLocation);
120-
73+
List<String> factoryNames = loadFactoryNames(factoryClass, classLoader);
12174
if (logger.isTraceEnabled()) {
122-
logger.trace(
123-
"Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
75+
logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
12476
}
125-
12677
List<T> result = new ArrayList<T>(factoryNames.size());
12778
for (String factoryName : factoryNames) {
12879
result.add(instantiateFactory(factoryName, factoryClass, classLoader));
12980
}
130-
131-
Collections.sort(result, new AnnotationAwareOrderComparator());
132-
81+
OrderComparator.sort(result);
13382
return result;
13483
}
13584

136-
private static List<String> loadFactoryNames(Class<?> factoryClass,
137-
ClassLoader classLoader, String factoriesLocation) {
138-
85+
private static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
13986
String factoryClassName = factoryClass.getName();
140-
14187
try {
14288
List<String> result = new ArrayList<String>();
143-
Enumeration<URL> urls = classLoader.getResources(factoriesLocation);
89+
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
14490
while (urls.hasMoreElements()) {
14591
URL url = urls.nextElement();
146-
Properties properties =
147-
PropertiesLoaderUtils.loadProperties(new UrlResource(url));
92+
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
14893
String factoryClassNames = properties.getProperty(factoryClassName);
149-
result.addAll(Arrays.asList(
150-
StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
94+
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
15195
}
15296
return result;
15397
}
15498
catch (IOException ex) {
155-
throw new IllegalArgumentException(
156-
"Unable to load [" + factoryClass.getName() +
157-
"] factories from location [" +
158-
factoriesLocation + "]", ex);
99+
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
100+
"] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
159101
}
160-
161-
162102
}
163103

164104
@SuppressWarnings("unchecked")
165-
private static <T> T instantiateFactory(String instanceClassName,
166-
Class<T> factoryClass, ClassLoader classLoader) {
105+
private static <T> T instantiateFactory(String instanceClassName, Class<T> factoryClass, ClassLoader classLoader) {
167106
try {
168107
Class<?> instanceClass = ClassUtils.forName(instanceClassName, classLoader);
169108
if (!factoryClass.isAssignableFrom(instanceClass)) {
170109
throw new IllegalArgumentException(
171-
"Class [" + instanceClassName + "] is not assignable to [" +
172-
factoryClass.getName() + "]");
110+
"Class [" + instanceClassName + "] is not assignable to [" + factoryClass.getName() + "]");
173111
}
174112
return (T) instanceClass.newInstance();
175113
}
176-
catch (ClassNotFoundException ex) {
177-
throw new IllegalArgumentException(
178-
factoryClass.getName() + " class [" + instanceClassName +
179-
"] not found", ex);
180-
}
181-
catch (LinkageError err) {
182-
throw new IllegalArgumentException(
183-
"Invalid " + factoryClass.getName() + " class [" + instanceClassName +
184-
"]: problem with handler class file or dependent class", err);
185-
}
186-
catch (InstantiationException ex) {
187-
throw new IllegalArgumentException(
188-
"Could not instantiate bean class [" + instanceClassName +
189-
"]: Is it an abstract class?", ex);
190-
}
191-
catch (IllegalAccessException ex) {
192-
throw new IllegalArgumentException(
193-
"Could not instantiate bean class [" + instanceClassName +
194-
"Is the constructor accessible?", ex);
114+
catch (Throwable ex) {
115+
throw new IllegalArgumentException("Cannot instantiate factory class: " + factoryClass.getName(), ex);
195116
}
196117
}
197118

198-
}
119+
}

spring-core/src/test/java/org/springframework/core/io/support/SpringFactoriesLoaderTests.java

Lines changed: 0 additions & 47 deletions
This file was deleted.

0 commit comments

Comments
 (0)