Skip to content

Commit 8adfe47

Browse files
committed
Improve ClassGeneratingEntityInstantiator error message if the to be instantiated type is abstract.
We now throw MappingInstantiationException with a nested BeanInstantiationException in alignment to ReflectionEntityInstantiator when attempting to create a new instance of an abstract class. Closes #2348.
1 parent d27ac79 commit 8adfe47

File tree

2 files changed

+71
-19
lines changed

2 files changed

+71
-19
lines changed

src/main/java/org/springframework/data/mapping/model/ClassGeneratingEntityInstantiator.java

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@
2626

2727
import org.apache.commons.logging.Log;
2828
import org.apache.commons.logging.LogFactory;
29+
2930
import org.springframework.asm.ClassWriter;
3031
import org.springframework.asm.MethodVisitor;
3132
import org.springframework.asm.Opcodes;
3233
import org.springframework.asm.Type;
34+
import org.springframework.beans.BeanInstantiationException;
3335
import org.springframework.cglib.core.ReflectUtils;
3436
import org.springframework.core.NativeDetector;
3537
import org.springframework.data.mapping.PersistentEntity;
@@ -121,6 +123,10 @@ private EntityInstantiator createEntityInstantiator(PersistentEntity<?, ?> entit
121123
return ReflectionEntityInstantiator.INSTANCE;
122124
}
123125

126+
if (Modifier.isAbstract(entity.getType().getModifiers())) {
127+
return MappingInstantiationExceptionEntityInstantiator.create(entity.getType());
128+
}
129+
124130
try {
125131
return doCreateEntityInstantiator(entity);
126132
} catch (Throwable ex) {
@@ -240,30 +246,30 @@ public <T, E extends PersistentEntity<? extends T, P>, P extends PersistentPrope
240246
throw new MappingInstantiationException(entity, Arrays.asList(params), e);
241247
}
242248
}
249+
}
243250

244-
/**
245-
* Extracts the arguments required to invoke the given constructor from the given {@link ParameterValueProvider}.
246-
*
247-
* @param constructor can be {@literal null}.
248-
* @param provider can be {@literal null}.
249-
* @return
250-
*/
251-
private <P extends PersistentProperty<P>, T> Object[] extractInvocationArguments(
252-
@Nullable PreferredConstructor<? extends T, P> constructor, ParameterValueProvider<P> provider) {
253-
254-
if (constructor == null || !constructor.hasParameters()) {
255-
return allocateArguments(0);
256-
}
251+
/**
252+
* Extracts the arguments required to invoke the given constructor from the given {@link ParameterValueProvider}.
253+
*
254+
* @param constructor can be {@literal null}.
255+
* @param provider can be {@literal null}.
256+
* @return
257+
*/
258+
static <P extends PersistentProperty<P>, T> Object[] extractInvocationArguments(
259+
@Nullable PreferredConstructor<? extends T, P> constructor, ParameterValueProvider<P> provider) {
257260

258-
Object[] params = allocateArguments(constructor.getConstructor().getParameterCount());
261+
if (constructor == null || !constructor.hasParameters()) {
262+
return allocateArguments(0);
263+
}
259264

260-
int index = 0;
261-
for (Parameter<?, P> parameter : constructor.getParameters()) {
262-
params[index++] = provider.getParameterValue(parameter);
263-
}
265+
Object[] params = allocateArguments(constructor.getConstructor().getParameterCount());
264266

265-
return params;
267+
int index = 0;
268+
for (Parameter<?, P> parameter : constructor.getParameters()) {
269+
params[index++] = provider.getParameterValue(parameter);
266270
}
271+
272+
return params;
267273
}
268274

269275
/**
@@ -276,6 +282,40 @@ public interface ObjectInstantiator {
276282
Object newInstance(Object... args);
277283
}
278284

285+
/**
286+
* {@link EntityInstantiator} throwing {@link MappingInstantiationException} upon
287+
* {@link #createInstance(PersistentEntity, ParameterValueProvider)}.
288+
*
289+
* @author Mark Paluch
290+
* @since 2.5
291+
*/
292+
static class MappingInstantiationExceptionEntityInstantiator implements EntityInstantiator {
293+
294+
private final Class<?> typeToCreate;
295+
296+
private MappingInstantiationExceptionEntityInstantiator(Class<?> typeToCreate) {
297+
this.typeToCreate = typeToCreate;
298+
}
299+
300+
public static EntityInstantiator create(Class<?> typeToCreate) {
301+
return new MappingInstantiationExceptionEntityInstantiator(typeToCreate);
302+
}
303+
304+
/*
305+
* (non-Javadoc)
306+
* @see org.springframework.data.mapping.model.EntityInstantiator#createInstance(org.springframework.data.mapping.PersistentEntity, org.springframework.data.mapping.model.ParameterValueProvider)
307+
*/
308+
@Override
309+
public <T, E extends PersistentEntity<? extends T, P>, P extends PersistentProperty<P>> T createInstance(E entity,
310+
ParameterValueProvider<P> provider) {
311+
312+
Object[] params = extractInvocationArguments(entity.getPersistenceConstructor(), provider);
313+
314+
throw new MappingInstantiationException(entity, Arrays.asList(params),
315+
new BeanInstantiationException(typeToCreate, "Class is abstract"));
316+
}
317+
}
318+
279319
/**
280320
* Generates a new {@link ObjectInstantiator} class for the given custom class.
281321
* <p>

src/test/java/org/springframework/data/mapping/model/ClassGeneratingEntityInstantiatorUnitTests.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.springframework.data.mapping.PreferredConstructor.Parameter;
4040
import org.springframework.data.mapping.model.ClassGeneratingEntityInstantiator.ObjectInstantiator;
4141
import org.springframework.data.mapping.model.ClassGeneratingEntityInstantiatorUnitTests.Outer.Inner;
42+
import org.springframework.data.util.ClassTypeInformation;
4243
import org.springframework.util.ReflectionUtils;
4344

4445
/**
@@ -374,6 +375,13 @@ void shouldUseReflectionIfFrameworkTypesNotVisible() throws Exception {
374375
assertThat(this.instance.shouldUseReflectionEntityInstantiator(entity)).isTrue();
375376
}
376377

378+
@Test // GH-2348
379+
void entityInstantiatorShouldFailForAbstractClass() {
380+
381+
assertThatExceptionOfType(MappingInstantiationException.class).isThrownBy(() -> this.instance
382+
.createInstance(new BasicPersistentEntity<>(ClassTypeInformation.from(AbstractDto.class)), provider));
383+
}
384+
377385
private void prepareMocks(Class<?> type) {
378386

379387
doReturn(type).when(entity).getType();
@@ -519,4 +527,8 @@ static class ClassWithPackagePrivateConstructor {
519527
ClassWithPackagePrivateConstructor() {}
520528
}
521529

530+
public static abstract class AbstractDto {
531+
532+
}
533+
522534
}

0 commit comments

Comments
 (0)