Bug 482698 Improve Injector performance.
Change-Id: Ib46c04d285fc8916383cb2aa97765ab5862ca92c
Signed-off-by: Ed Merks <ed.merks@gmail.com>
diff --git a/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/ClassRequestor.java b/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/ClassRequestor.java
index d913dc9..10016d9 100644
--- a/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/ClassRequestor.java
+++ b/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/ClassRequestor.java
@@ -30,6 +30,8 @@
@Named("e4.internal.injectionLink")
final static public String pseudoVariable = null;
+ private static IObjectDescriptor[] pseudoVariableDescriptor;
+
public ClassRequestor(Class<?> clazz, IInjector injector, PrimaryObjectSupplier primarySupplier, PrimaryObjectSupplier tempSupplier, Object requestingObject, boolean track) {
super(clazz, injector, primarySupplier, tempSupplier, requestingObject, track);
}
@@ -42,17 +44,22 @@
@Override
public IObjectDescriptor[] calcDependentObjects() {
- Field field = null;
- try {
- field = ClassRequestor.class.getField("pseudoVariable"); //$NON-NLS-1$
- } catch (SecurityException e) {
- e.printStackTrace(); // tested - not going to happen
- return null;
- } catch (NoSuchFieldException e) {
- e.printStackTrace(); // tested - not going to happen
- return null;
+ if (pseudoVariableDescriptor == null) {
+ Field field = null;
+ try {
+ field = ClassRequestor.class.getField("pseudoVariable"); //$NON-NLS-1$
+ } catch (SecurityException e) {
+ e.printStackTrace(); // tested - not going to happen
+ return null;
+ } catch (NoSuchFieldException e) {
+ e.printStackTrace(); // tested - not going to happen
+ return null;
+ }
+ pseudoVariableDescriptor = new IObjectDescriptor[] {
+ new ObjectDescriptor(field.getGenericType(), field.getAnnotations()) };
}
- return new IObjectDescriptor[] {new ObjectDescriptor(field.getGenericType(), field.getAnnotations())};
+
+ return pseudoVariableDescriptor;
}
@Override
diff --git a/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/InjectorImpl.java b/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/InjectorImpl.java
index 5a86dbb..25b2112 100644
--- a/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/InjectorImpl.java
+++ b/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/InjectorImpl.java
@@ -13,6 +13,7 @@
import java.lang.annotation.Annotation;
import java.lang.ref.WeakReference;
+import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@@ -99,9 +100,12 @@
private Set<WeakReference<Class<?>>> injectedClasses = new HashSet<WeakReference<Class<?>>>();
private HashMap<Class<?>, Object> singletonCache = new HashMap<Class<?>, Object>();
private Map<Class<?>, Set<Binding>> bindings = new HashMap<Class<?>, Set<Binding>>();
+ private Map<Class<? extends Annotation>, Map<AnnotatedElement, Boolean>> annotationsPresent = new HashMap<>();
// Performance improvement:
private Map<Class<?>, Method[]> methodsCache = new WeakHashMap<Class<?>, Method[]>();
+ private Map<Class<?>, Field[]> fieldsCache = new WeakHashMap<Class<?>, Field[]>();
+ private Map<Class<?>, Constructor<?>[]> constructorsCache = new WeakHashMap<Class<?>, Constructor<?>[]>();
private Map<Class<?>, Map<Method, Boolean>> isOverriddenCache = new WeakHashMap<Class<?>, Map<Method, Boolean>>();
private Set<Class<?>> classesBeingCreated = new HashSet<Class<?>>(5);
@@ -330,7 +334,7 @@
if (shouldDebug)
classesBeingCreated.add(clazz);
- boolean isSingleton = clazz.isAnnotationPresent(Singleton.class);
+ boolean isSingleton = isAnnotationPresent(clazz, Singleton.class);
if (isSingleton) {
synchronized (singletonCache) {
if (singletonCache.containsKey(clazz))
@@ -338,7 +342,7 @@
}
}
- Constructor<?>[] constructors = clazz.getDeclaredConstructors();
+ Constructor<?>[] constructors = getDeclaredConstructors(clazz);
// Sort the constructors by descending number of constructor arguments
ArrayList<Constructor<?>> sortedConstructors = new ArrayList<Constructor<?>>(constructors.length);
for (Constructor<?> constructor : constructors)
@@ -359,7 +363,7 @@
continue;
// unless this is the default constructor, it has to be tagged
- if (!constructor.isAnnotationPresent(Inject.class) && constructor.getParameterTypes().length != 0)
+ if (!isAnnotationPresent(constructor, Inject.class) && constructor.getParameterTypes().length != 0)
continue;
ConstructorRequestor requestor = new ConstructorRequestor(constructor, this, objectSupplier, tempSupplier);
@@ -594,12 +598,8 @@
if (qualifiers == null)
return null;
for (Annotation qualifier : qualifiers) {
- String key;
- Type type = qualifier.annotationType();
- if (type instanceof Class<?>) {
- key = ((Class<?>) type).getName();
- } else
- continue;
+ Class<?> type = qualifier.annotationType();
+ String key = ((Class<?>) type).getName();
ExtendedObjectSupplier supplier;
try {
@@ -676,14 +676,14 @@
*/
private boolean processFields(Object userObject, PrimaryObjectSupplier objectSupplier, PrimaryObjectSupplier tempSupplier, Class<?> objectsClass, boolean track, List<Requestor<?>> requestors) {
boolean injectedStatic = false;
- Field[] fields = objectsClass.getDeclaredFields();
+ Field[] fields = getDeclaredFields(objectsClass);
for (Field field : fields) {
if (Modifier.isStatic(field.getModifiers())) {
if (hasInjectedStatic(objectsClass))
continue;
injectedStatic = true;
}
- if (!field.isAnnotationPresent(Inject.class))
+ if (!isAnnotationPresent(field, Inject.class))
continue;
requestors.add(new FieldRequestor(field, this, objectSupplier, tempSupplier, userObject, track));
}
@@ -722,7 +722,7 @@
continue;
injectedStatic = true;
}
- if (!method.isAnnotationPresent(Inject.class))
+ if (!isAnnotationPresent(method, Inject.class))
continue;
requestors.add(new MethodRequestor(method, this, objectSupplier, tempSupplier, userObject, track));
}
@@ -783,6 +783,25 @@
return false;
}
+ private Constructor<?>[] getDeclaredConstructors(Class<?> c) {
+ Constructor<?>[] constructors = constructorsCache.get(c);
+ if (constructors == null) {
+ constructors = c.getDeclaredConstructors();
+ // Sort the constructors by descending number of constructor
+ // arguments
+ Arrays.sort(constructors, new Comparator<Constructor<?>>() {
+ @Override
+ public int compare(Constructor<?> c1, Constructor<?> c2) {
+ int l1 = c1.getParameterTypes().length;
+ int l2 = c2.getParameterTypes().length;
+ return l2 - l1;
+ }
+ });
+ constructorsCache.put(c, constructors);
+ }
+ return constructors;
+ }
+
private Method[] getDeclaredMethods(Class<?> c) {
Method[] methods = methodsCache.get(c);
if (methods == null) {
@@ -792,6 +811,15 @@
return methods;
}
+ private Field[] getDeclaredFields(Class<?> c) {
+ Field[] fields = fieldsCache.get(c);
+ if (fields == null) {
+ fields = c.getDeclaredFields();
+ fieldsCache.put(c, fields);
+ }
+ return fields;
+ }
+
private Class<?> getDesiredClass(Type desiredType) {
if (desiredType instanceof Class<?>)
return (Class<?>) desiredType;
@@ -909,7 +937,7 @@
}
Method[] methods = getDeclaredMethods(objectClass);
for (Method method : methods) {
- if (!method.isAnnotationPresent(annotation)) {
+ if (!isAnnotationPresent(method, annotation)) {
if (shouldDebug) {
for (Annotation a : method.getAnnotations()) {
if (annotation.getName().equals(a.annotationType().getName())) {
@@ -934,7 +962,7 @@
Object[] actualArgs = resolveArgs(requestor, objectSupplier, tempSupplier, false, false, false);
int unresolved = unresolved(actualArgs);
if (unresolved != -1) {
- if (method.isAnnotationPresent(Optional.class))
+ if (isAnnotationPresent(method, Optional.class))
continue;
reportUnresolvedArgument(requestor, unresolved);
}
@@ -964,4 +992,21 @@
defaultSupplier = objectSupplier;
}
+ private boolean isAnnotationPresent(AnnotatedElement annotatedElement,
+ Class<? extends Annotation> annotation) {
+ Map<AnnotatedElement, Boolean> cache = annotationsPresent.get(annotation);
+ if (cache == null) {
+ cache = new WeakHashMap<>();
+ annotationsPresent.put(annotation, cache);
+ }
+
+ Boolean present = cache.get(annotatedElement);
+ if (present != null) {
+ return present;
+ }
+
+ boolean isPresent = annotatedElement.isAnnotationPresent(annotation);
+ cache.put(annotatedElement, isPresent);
+ return isPresent;
+ }
}
diff --git a/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/Requestor.java b/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/Requestor.java
index f5f578e..cba3b11 100644
--- a/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/Requestor.java
+++ b/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/Requestor.java
@@ -13,6 +13,9 @@
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.lang.reflect.AnnotatedElement;
+import java.util.Collections;
+import java.util.Map;
+import java.util.WeakHashMap;
import org.eclipse.e4.core.di.IInjector;
import org.eclipse.e4.core.di.annotations.GroupUpdates;
import org.eclipse.e4.core.di.annotations.Optional;
@@ -25,6 +28,9 @@
*/
abstract public class Requestor<L extends AnnotatedElement> implements IRequestor {
+ private static final Map<AnnotatedElement, IObjectDescriptor[]> descriptorCache = Collections
+ .synchronizedMap(new WeakHashMap<>());
+
/** The request location; may be null */
final protected L location;
final private WeakReference<Object> objectRef;
@@ -165,9 +171,10 @@
return false;
}
- public IObjectDescriptor[] getDependentObjects() {
- if (objectDescriptors == null)
- objectDescriptors = calcDependentObjects();
+ public synchronized IObjectDescriptor[] getDependentObjects() {
+ if (objectDescriptors == null) {
+ objectDescriptors = descriptorCache.computeIfAbsent(location, l -> calcDependentObjects());
+ }
return objectDescriptors;
}