1313
1414import java .lang .reflect .Method ;
1515import java .lang .reflect .Modifier ;
16+ import java .util .concurrent .ConcurrentHashMap ;
1617
1718/**
1819 * A generic object factory to create an object of class T. T must be a concrete
4445 */
4546public class ObjFactory {
4647
48+ private static final int CACHE_INITIAL_CAPACITY = 4096 ;
49+ private static final float CACHE_LOAD_FACTOR = 0.75F ;
50+ private static final ConcurrentHashMap <String ,Class <?>> CLASSES_CACHE = new ConcurrentHashMap <>(CACHE_INITIAL_CAPACITY , CACHE_LOAD_FACTOR );
51+ private static final ConcurrentHashMap <String ,MethodWrappedInfo > METHODS_CACHE = new ConcurrentHashMap <>(CACHE_INITIAL_CAPACITY , CACHE_LOAD_FACTOR );
52+
4753 /**
4854 * Create an object based on the <code>className</code> parameter.
4955 *
@@ -70,20 +76,13 @@ public static <T> T make(String className, String typeName) throws Configuration
7076 // No big deal...just use "[unknown?]" for this as it's only for an err msg.
7177 typeName = "[unknown?]" ; // CHECKME: Any better suggestions?
7278 }
73-
74- Class <?> theClass = Class .forName (className );
75-
76- try {
77- Method singleton = theClass .getMethod ( "getInstance" );
78-
79- // If the implementation class contains a getInstance method that is not static, this is an invalid
80- // object configuration and a ConfigurationException will be thrown.
81- if ( !Modifier .isStatic ( singleton .getModifiers () ) )
82- {
83- throw new ConfigurationException ( "Class [" + className + "] contains a non-static getInstance method." );
84- }
85-
86- obj = singleton .invoke ( null );
79+
80+ Class <?> theClass = loadClassByStringName (className );
81+
82+ try {
83+ Method singleton = findSingletonCreateMethod (className , theClass );
84+
85+ obj = singleton .invoke ( null );
8786 } catch (NoSuchMethodException e ) {
8887 // This is a no-error exception, if this is caught we will continue on assuming the implementation was
8988 // not meant to be used as a singleton.
@@ -97,7 +96,7 @@ public static <T> T make(String className, String typeName) throws Configuration
9796 }
9897
9998 return (T )obj ; // Eclipse warning here if @SupressWarnings omitted.
100-
99+
101100 // Issue 66 - Removed System.out calls as we are throwing an exception in each of these cases
102101 // anyhow.
103102 } catch ( IllegalArgumentException ex ) {
@@ -130,9 +129,98 @@ public static <T> T make(String className, String typeName) throws Configuration
130129 }
131130 // DISCUSS: Should we also catch ExceptionInInitializerError here? See Google Issue #61 comments.
132131 }
133-
132+
133+ /**
134+ * Load the class in cache, or load by the classloader and cache it
135+ *
136+ * @param className The name of the class to construct. Should be a fully qualified name
137+ * @return The target class
138+ * @throws ClassNotFoundException Failed to load class by the className
139+ */
140+ private static Class <?> loadClassByStringName (String className ) throws ClassNotFoundException {
141+ Class <?> clazz ;
142+ if (CLASSES_CACHE .containsKey (className )) {
143+ clazz = CLASSES_CACHE .get (className );
144+ } else {
145+ clazz = Class .forName (className );
146+ CLASSES_CACHE .putIfAbsent (className , clazz );
147+ }
148+ return clazz ;
149+ }
150+
134151 /**
152+ * Find the method to create a singleton object
153+ *
154+ * @param className The name of the class to construct. Should be a fully qualified name
155+ * @param theClass The class loaded in prior
156+ * @return The method to create a singleton object
157+ * @throws NoSuchMethodException Failed to find the target method
158+ */
159+ private static Method findSingletonCreateMethod (String className , Class <?> theClass ) throws NoSuchMethodException {
160+ MethodWrappedInfo singleton = loadMethodByStringName (className ,theClass );
161+
162+ // If the implementation class contains a getInstance method that is not static, this is an invalid
163+ // object configuration and a ConfigurationException will be thrown.
164+ if (!singleton .isStaticMethod ()) {
165+ throw singleton .getNonStaticEx ();
166+ }
167+ return singleton .getMethod ();
168+ }
169+
170+ /**
171+ *
172+ * @param className The name of the class to construct. Should be a fully qualified name
173+ * @param theClass The class loaded in prior
174+ * @return Wrapped data, contains the method object and the method is static method or not
175+ * @throws NoSuchMethodException Failed to find the target method
176+ */
177+ private static MethodWrappedInfo loadMethodByStringName (String className , Class <?> theClass ) throws NoSuchMethodException {
178+ String methodName = className + "getInstance" ;
179+ MethodWrappedInfo methodInfo ;
180+ if (METHODS_CACHE .containsKey (methodName )) {
181+ methodInfo = METHODS_CACHE .get (methodName );
182+ } else {
183+ Method method = theClass .getMethod ("getInstance" );
184+ boolean staticMethod = Modifier .isStatic (method .getModifiers ());
185+ ConfigurationException nonStaticEx = staticMethod ? null :
186+ new ConfigurationException ("Class [" + className + "] contains a non-static getInstance method." );
187+ methodInfo = new MethodWrappedInfo (method , staticMethod , nonStaticEx );
188+ METHODS_CACHE .putIfAbsent (methodName , methodInfo );
189+ }
190+ return methodInfo ;
191+ }
192+
193+ /**
135194 * Not instantiable
136195 */
137196 private ObjFactory () { }
197+
198+ /**
199+ * Wrapped data, contains the method object and the method is static method or not.<br>
200+ * The goal to store the boolean value in field staticMethod is reduce the check times: check once, use many times.<br>
201+ * The goal to store the exception in field nonStaticException is reduce the cost of new Exception(): create once, use many times.
202+ */
203+ private static class MethodWrappedInfo {
204+ private Method method ;
205+ private boolean staticMethod ;
206+ private ConfigurationException nonStaticEx ;
207+
208+ MethodWrappedInfo (Method method , boolean staticMethod , ConfigurationException nonStaticEx ) {
209+ this .method = method ;
210+ this .staticMethod = staticMethod ;
211+ this .nonStaticEx = nonStaticEx ;
212+ }
213+
214+ Method getMethod () {
215+ return method ;
216+ }
217+
218+ boolean isStaticMethod () {
219+ return staticMethod ;
220+ }
221+
222+ ConfigurationException getNonStaticEx () {
223+ return nonStaticEx ;
224+ }
225+ }
138226}
0 commit comments