1
+ /*
2
+ * Copyright 2002-2012 the original author or authors.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ package org .springframework .core ;
18
+
19
+ import java .io .IOException ;
20
+ import java .net .URL ;
21
+ import java .util .ArrayList ;
22
+ import java .util .Arrays ;
23
+ import java .util .Collections ;
24
+ import java .util .Enumeration ;
25
+ import java .util .List ;
26
+ import java .util .Properties ;
27
+
28
+ import org .apache .commons .logging .Log ;
29
+ import org .apache .commons .logging .LogFactory ;
30
+
31
+ import org .springframework .core .io .UrlResource ;
32
+ import org .springframework .core .io .support .PropertiesLoaderUtils ;
33
+ import org .springframework .util .Assert ;
34
+ import org .springframework .util .ClassUtils ;
35
+ import org .springframework .util .StringUtils ;
36
+
37
+ /**
38
+ * General purpose factory loading mechanism.
39
+ *
40
+ * <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 keys is the fully
45
+ * qualified interface or abstract class name, and the value is a comma separated list of
46
+ * implementations. For instance:
47
+ * <pre class="code">
48
+ * example.MyService=example.MyServiceImpl1,example.MyServiceImpl2
49
+ * </pre>
50
+ * where {@code MyService} is the name of the interface, and {@code MyServiceImpl1} and
51
+ * {@code MyServiceImpl2} are the two implementations.
52
+ *
53
+ * @author Arjen Poutsma
54
+ * @since 3.2
55
+ */
56
+ public abstract class SpringFactoriesLoader {
57
+
58
+ private static final Log logger = LogFactory .getLog (SpringFactoriesLoader .class );
59
+
60
+ /** 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" ;
62
+
63
+ /**
64
+ * Loads the factory implementations of the given type from the default location, using
65
+ * the given class loader.
66
+ *
67
+ * <p>The returned factories are ordered in accordance with the {@link OrderComparator}.
68
+ *
69
+ * @param factoryClass the interface or abstract class representing the factory
70
+ * @param classLoader the ClassLoader to use for loading, can be {@code null} to use the
71
+ * default
72
+ * @throws IllegalArgumentException in case of errors
73
+ */
74
+ public static <T > List <T > loadFactories (Class <T > factoryClass ,
75
+ ClassLoader classLoader ) {
76
+ return loadFactories (factoryClass , classLoader , null );
77
+ }
78
+
79
+ /**
80
+ * Loads the factory implementations of the given type from the given location, using the
81
+ * given class loader.
82
+ *
83
+ * <p>The returned factories are ordered in accordance with the {@link OrderComparator}.
84
+ *
85
+ * @param factoryClass the interface or abstract class representing the factory
86
+ * @param classLoader the ClassLoader to use for loading, can be {@code null} to
87
+ * use the default
88
+ * @param factoriesLocation the factories file location, can be {@code null} to use the
89
+ * {@linkplain #DEFAULT_FACTORIES_LOCATION default}
90
+ * @throws IllegalArgumentException in case of errors
91
+ */
92
+ public static <T > List <T > loadFactories (Class <T > factoryClass ,
93
+ ClassLoader classLoader ,
94
+ String factoriesLocation ) {
95
+ Assert .notNull (factoryClass , "'factoryClass' must not be null" );
96
+
97
+ if (classLoader == null ) {
98
+ classLoader = ClassUtils .getDefaultClassLoader ();
99
+ }
100
+ if (factoriesLocation == null ) {
101
+ factoriesLocation = DEFAULT_FACTORIES_LOCATION ;
102
+ }
103
+
104
+ List <String > factoryNames =
105
+ loadFactoryNames (factoryClass , classLoader , factoriesLocation );
106
+
107
+ if (logger .isTraceEnabled ()) {
108
+ logger .trace (
109
+ "Loaded [" + factoryClass .getName () + "] names: " + factoryNames );
110
+ }
111
+
112
+ List <T > result = new ArrayList <T >(factoryNames .size ());
113
+ for (String factoryName : factoryNames ) {
114
+ result .add (instantiateFactory (factoryName , factoryClass , classLoader ));
115
+ }
116
+
117
+ Collections .sort (result , new OrderComparator ());
118
+
119
+ return result ;
120
+ }
121
+
122
+ private static List <String > loadFactoryNames (Class factoryClass ,
123
+ ClassLoader classLoader ,
124
+ String factoriesLocation ) {
125
+
126
+ String factoryClassName = factoryClass .getName ();
127
+
128
+ try {
129
+ List <String > result = new ArrayList <String >();
130
+ Enumeration urls = classLoader .getResources (factoriesLocation );
131
+ while (urls .hasMoreElements ()) {
132
+ URL url = (URL ) urls .nextElement ();
133
+ Properties properties =
134
+ PropertiesLoaderUtils .loadProperties (new UrlResource (url ));
135
+ String factoryClassNames = properties .getProperty (factoryClassName );
136
+ result .addAll (Arrays .asList (
137
+ StringUtils .commaDelimitedListToStringArray (factoryClassNames )));
138
+ }
139
+ return result ;
140
+ }
141
+ catch (IOException ex ) {
142
+ throw new IllegalArgumentException (
143
+ "Unable to load [" + factoryClass .getName () +
144
+ "] factories from location [" +
145
+ factoriesLocation + "]" , ex );
146
+ }
147
+
148
+
149
+ }
150
+
151
+ @ SuppressWarnings ("unchecked" )
152
+ private static <T > T instantiateFactory (String instanceClassName ,
153
+ Class <T > factoryClass ,
154
+ ClassLoader classLoader ) {
155
+ try {
156
+ Class <?> instanceClass = ClassUtils .forName (instanceClassName , classLoader );
157
+ if (!factoryClass .isAssignableFrom (instanceClass )) {
158
+ throw new IllegalArgumentException (
159
+ "Class [" + instanceClassName + "] is not assignable to [" +
160
+ factoryClass .getName () + "]" );
161
+ }
162
+ return (T ) instanceClass .newInstance ();
163
+ }
164
+ catch (ClassNotFoundException ex ) {
165
+ throw new IllegalArgumentException (
166
+ factoryClass .getName () + " class [" + instanceClassName +
167
+ "] not found" , ex );
168
+ }
169
+ catch (LinkageError err ) {
170
+ throw new IllegalArgumentException (
171
+ "Invalid " + factoryClass .getName () + " class [" + instanceClassName +
172
+ "]: problem with handler class file or dependent class" , err );
173
+ }
174
+ catch (InstantiationException ex ) {
175
+ throw new IllegalArgumentException (
176
+ "Could not instantiate bean class [" + instanceClassName +
177
+ "]: Is it an abstract class?" , ex );
178
+ }
179
+ catch (IllegalAccessException ex ) {
180
+ throw new IllegalArgumentException (
181
+ "Could not instantiate bean class [" + instanceClassName +
182
+ "Is the constructor accessible?" , ex );
183
+ }
184
+ }
185
+
186
+ }
0 commit comments