From 8fa7aabdc0e230c548fd66a4575a31509857f5dd Mon Sep 17 00:00:00 2001 From: Michael Hunger Date: Thu, 24 Mar 2011 22:36:49 +0100 Subject: [PATCH] added caching to AbstractConstructorEntityInstantiator --- ...AbstractConstructorEntityInstantiator.java | 124 ++++++++++++------ 1 file changed, 86 insertions(+), 38 deletions(-) diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/persistence/AbstractConstructorEntityInstantiator.java b/spring-data-commons-core/src/main/java/org/springframework/data/persistence/AbstractConstructorEntityInstantiator.java index acefc539d..3684953a8 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/persistence/AbstractConstructorEntityInstantiator.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/persistence/AbstractConstructorEntityInstantiator.java @@ -1,12 +1,14 @@ package org.springframework.data.persistence; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.ClassUtils; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Map; + /** * Try for a constructor taking state: failing that, try a no-arg * constructor and then setUnderlyingNode(). @@ -14,49 +16,95 @@ import org.springframework.util.ClassUtils; * @author Rod Johnson */ public abstract class AbstractConstructorEntityInstantiator implements EntityInstantiator { - + + interface Instantiator { + T create(STATE n, Class c) throws Exception; + } + private final Logger log = LoggerFactory.getLogger(getClass()); - + private final Map,Instantiator> cache = new HashMap,Instantiator>(); + final public T createEntityFromState(STATE n, Class c) { try { - return fromStateInternal(n, c); - } catch (InstantiationException e) { - throw new IllegalArgumentException(e); - } catch (IllegalAccessException e) { - throw new IllegalArgumentException(e); - } catch (InvocationTargetException e) { + Instantiator instantiator = (Instantiator) cache.get(c); + if (instantiator!=null) return instantiator.create(n,c); + synchronized (cache) { + instantiator = (Instantiator) cache.get(c); + if (instantiator!=null) return instantiator.create(n,c); + Class stateClass = (Class) n.getClass(); + instantiator=createInstantiator(c, stateClass); + cache.put(c,instantiator); + return instantiator.create(n,c); + } + } catch (IllegalArgumentException e) { + throw e; + } catch (InvocationTargetException e) { + throw new IllegalArgumentException(e.getTargetException()); + } catch (Exception e) { throw new IllegalArgumentException(e); } } - final private T fromStateInternal(STATE n, Class c) throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { - // TODO this is fragile - Class stateInterface = (Class) n.getClass().getInterfaces()[0]; - Constructor nodeConstructor = ClassUtils.getConstructorIfAvailable(c, stateInterface); - if (nodeConstructor != null) { - // TODO is this the correct way to instantiate or does Spring have a preferred way? - log.info("Using " + c + " constructor taking " + stateInterface); - return nodeConstructor.newInstance(n); - } - - Constructor noArgConstructor = ClassUtils.getConstructorIfAvailable(c); - if (noArgConstructor == null) noArgConstructor = getDeclaredConstructor(c); - if (noArgConstructor != null) { - log.info("Using " + c + " no-arg constructor"); - StateProvider.setUnderlyingState(n); - T t; - try { - t = noArgConstructor.newInstance(); - setState(t, n); - } finally { - StateProvider.retrieveState(); + + public void setInstantiators(Map,Instantiator> instantiators) { + this.cache.putAll(instantiators); + } + + protected Instantiator createInstantiator(Class type, final Class stateType) { + Instantiator instantiator=stateTakingConstructorInstantiator(type,stateType); + if (instantiator!=null) return instantiator; + instantiator = emptyConstructorStateSettingInstantiator(type,stateType); + if (instantiator!=null) return instantiator; + return createFailingInstantiator(stateType); + } + + private Instantiator createFailingInstantiator(final Class stateType) { + return new Instantiator() { + public T create(STATE n, Class c) throws Exception { + throw new IllegalArgumentException(getClass().getSimpleName() + ": entity " + c + + " must have either a constructor taking [" + stateType + "] or a no-arg constructor and state set method"); } - return t; - } - - throw new IllegalArgumentException(getClass().getSimpleName() + ": entity " + c + " must have either a constructor taking [" + stateInterface + - "] or a no-arg constructor and state set method"); - } + }; + } + + private Instantiator emptyConstructorStateSettingInstantiator(Class type, Class stateType) { + final Constructor constructor = getNoArgConstructor(type); + if (constructor == null) return null; + + log.info("Using " + type + " no-arg constructor"); + + return new Instantiator() { + public T create(STATE n, Class c) throws Exception { + try { + StateProvider.setUnderlyingState(n); + T newInstance = constructor.newInstance(); + setState(newInstance, n); + return newInstance; + } finally { + StateProvider.retrieveState(); + } + } + }; + } + + private Constructor getNoArgConstructor(Class type) { + Constructor constructor = ClassUtils.getConstructorIfAvailable(type); + if (constructor != null) return constructor; + return getDeclaredConstructor(type); + } + + private Instantiator stateTakingConstructorInstantiator(Class type, Class stateType) { + Class stateInterface = (Class) stateType.getInterfaces()[0]; + final Constructor constructor = ClassUtils.getConstructorIfAvailable(type, stateInterface); + if (constructor == null) return null; + + log.info("Using " + type + " constructor taking " + stateInterface); + return new Instantiator() { + public T create(STATE n, Class c) throws Exception { + return constructor.newInstance(n); + } + }; + } private Constructor getDeclaredConstructor(Class c) { try {