Skip to content

Commit c30f3fc

Browse files
christophstroblmp911de
authored andcommitted
Create PersistentEntities only for user class in case of proxy instance.
This commit makes sure to only create a PersistentEntity for an actual user type. Therefore ClassTypeInformation now considers the given type and not only the user one for both equals and hashcode. This makes sure we can distinguish TypeInformation for a proxy from the one of a user class. The AbstractMapping context will take care of unpacking proxied types and registering the created entity for both the user as well as the proxy type information. Prior to this commit build time generated proxy instances would have been able to pollute the context depending on the order they had been served by the initial entity set. Closes #2485 Original pull request: #2486.
1 parent 69397a1 commit c30f3fc

File tree

5 files changed

+357
-1
lines changed

5 files changed

+357
-1
lines changed

src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,17 @@ protected Optional<E> addPersistentEntity(TypeInformation<?> typeInformation) {
360360
return persistentEntity;
361361
}
362362

363+
if(typeInformation.isProxyTypeInformation()) {
364+
365+
TypeInformation<?> userTypeInformation = typeInformation.getUserTypeInformation();
366+
persistentEntity = persistentEntities.get(userTypeInformation);
367+
368+
if (persistentEntity != null) {
369+
persistentEntities.put(typeInformation, persistentEntity);
370+
return persistentEntity;
371+
}
372+
}
373+
363374
} finally {
364375
read.unlock();
365376
}
@@ -369,7 +380,10 @@ protected Optional<E> addPersistentEntity(TypeInformation<?> typeInformation) {
369380
try {
370381

371382
write.lock();
372-
entity = doAddPersistentEntity(typeInformation);
383+
entity = doAddPersistentEntity(typeInformation.getUserTypeInformation());
384+
if(typeInformation.isProxyTypeInformation()) {
385+
persistentEntities.put(typeInformation, Optional.of(entity));
386+
}
373387

374388
} catch (BeansException e) {
375389
throw new MappingException(e.getMessage(), e);

src/main/java/org/springframework/data/util/ClassTypeInformation.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.springframework.util.Assert;
3333
import org.springframework.util.ConcurrentReferenceHashMap;
3434
import org.springframework.util.ConcurrentReferenceHashMap.ReferenceType;
35+
import org.springframework.util.ObjectUtils;
3536

3637
/**
3738
* {@link TypeInformation} for a plain {@link Class}.
@@ -177,4 +178,26 @@ public TypeInformation<? extends S> specialize(ClassTypeInformation<?> type) {
177178
public String toString() {
178179
return type.getName();
179180
}
181+
182+
@Override
183+
public boolean equals(Object o) {
184+
if (this == o) {
185+
return true;
186+
}
187+
188+
if (!super.equals(o)) {
189+
return false;
190+
}
191+
192+
ClassTypeInformation<?> that = (ClassTypeInformation<?>) o;
193+
return ObjectUtils.nullSafeEquals(type, that.type);
194+
}
195+
196+
@Override
197+
public int hashCode() {
198+
199+
int result = super.hashCode();
200+
result = 31 * result + ObjectUtils.nullSafeHashCode(type);
201+
return result;
202+
}
180203
}

src/main/java/org/springframework/data/util/TypeInformation.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,29 @@ default TypeInformation<?> getRequiredMapValueType() {
149149
*/
150150
Class<S> getType();
151151

152+
/**
153+
* Returns the user type of the property if proxied.
154+
*
155+
* @return the unpacked (if proxied) type of the property.
156+
* @see ProxyUtils#getUserClass(Class)
157+
* @since 2.6
158+
*/
159+
default TypeInformation<?> getUserTypeInformation() {
160+
161+
Class<?> userType = ProxyUtils.getUserClass(getType());
162+
return userType.equals(getType()) ? this : ClassTypeInformation.from(userType);
163+
}
164+
165+
/**
166+
* Returns if {@link #getType()} refers to a proxy or user class.
167+
*
168+
* @return true if type is a proxy.
169+
* @since 2.6
170+
*/
171+
default boolean isProxyTypeInformation() {
172+
return !this.equals(getUserTypeInformation());
173+
}
174+
152175
/**
153176
* Returns a {@link ClassTypeInformation} to represent the {@link TypeInformation} of the raw type of the current
154177
* instance.

src/test/java/org/springframework/data/mapping/context/AbstractMappingContextUnitTests.java

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,14 @@
3737
import java.util.TreeMap;
3838
import java.util.function.Supplier;
3939

40+
import org.aopalliance.aop.Advice;
4041
import org.junit.jupiter.api.BeforeEach;
4142
import org.junit.jupiter.api.Test;
43+
import org.springframework.aop.Advisor;
44+
import org.springframework.aop.SpringProxy;
45+
import org.springframework.aop.TargetSource;
46+
import org.springframework.aop.framework.Advised;
47+
import org.springframework.aop.framework.AopConfigException;
4248
import org.springframework.context.ApplicationContext;
4349
import org.springframework.context.ApplicationEvent;
4450
import org.springframework.data.annotation.Id;
@@ -54,6 +60,7 @@
5460
import org.springframework.data.util.ClassTypeInformation;
5561
import org.springframework.data.util.StreamUtils;
5662
import org.springframework.data.util.TypeInformation;
63+
import org.springframework.lang.Nullable;
5764

5865
/**
5966
* Unit test for {@link AbstractMappingContext}.
@@ -323,6 +330,40 @@ void shouldNotCreatePersistentEntityForMapButItsGenericTypeArguments() {
323330
.doesNotContain(List.class, Map.class, String.class, Integer.class);
324331
}
325332

333+
@Test // GH-2485
334+
void contextSeesUserTypeBeforeProxy() {
335+
336+
SampleMappingContext context = new SampleMappingContext();
337+
BasicPersistentEntity<Object, SamplePersistentProperty> persistentEntity = context.getPersistentEntity(Base.class);
338+
persistentEntity.getTypeInformation().getType().equals(Base.class);
339+
340+
assertThat(context.hasPersistentEntityFor(Base.class)).isTrue();
341+
assertThat(context.hasPersistentEntityFor(Base$$SpringProxy$873fa2e.class)).isFalse();
342+
343+
BasicPersistentEntity<Object, SamplePersistentProperty> persistentEntityForProxy = context.getPersistentEntity(Base$$SpringProxy$873fa2e.class);
344+
persistentEntityForProxy.getTypeInformation().getType().equals(Base.class);
345+
assertThat(context.hasPersistentEntityFor(Base$$SpringProxy$873fa2e.class)).isTrue();
346+
347+
assertThat(context.getPersistentEntities()).hasSize(1); // only one distinct instance
348+
}
349+
350+
@Test // GH-2485
351+
void contextSeesProxyBeforeUserType() {
352+
353+
SampleMappingContext context = new SampleMappingContext();
354+
355+
BasicPersistentEntity<Object, SamplePersistentProperty> persistentEntityForProxy = context.getPersistentEntity(Base$$SpringProxy$873fa2e.class);
356+
persistentEntityForProxy.getTypeInformation().getType().equals(Base.class);
357+
358+
assertThat(context.hasPersistentEntityFor(Base$$SpringProxy$873fa2e.class)).isTrue();
359+
assertThat(context.hasPersistentEntityFor(Base.class)).isTrue();
360+
361+
BasicPersistentEntity<Object, SamplePersistentProperty> persistentEntity = context.getPersistentEntity(Base.class);
362+
persistentEntity.getTypeInformation().getType().equals(Base.class);
363+
364+
assertThat(context.getPersistentEntities()).hasSize(1); // only one distinct instance
365+
}
366+
326367
private static void assertHasEntityFor(Class<?> type, SampleMappingContext context, boolean expected) {
327368

328369
boolean found = false;
@@ -505,4 +546,123 @@ class WithMap {
505546
Map<MapKey, Integer> mapOfKeyToPerson;
506547
}
507548

549+
static class Base$$SpringProxy$873fa2e extends Base implements SpringProxy, Advised {
550+
551+
@Override
552+
public boolean isFrozen() {
553+
return false;
554+
}
555+
556+
@Override
557+
public boolean isProxyTargetClass() {
558+
return false;
559+
}
560+
561+
@Override
562+
public Class<?>[] getProxiedInterfaces() {
563+
return new Class[0];
564+
}
565+
566+
@Override
567+
public boolean isInterfaceProxied(Class<?> intf) {
568+
return false;
569+
}
570+
571+
@Override
572+
public void setTargetSource(TargetSource targetSource) {
573+
574+
}
575+
576+
@Override
577+
public TargetSource getTargetSource() {
578+
return null;
579+
}
580+
581+
@Override
582+
public void setExposeProxy(boolean exposeProxy) {
583+
584+
}
585+
586+
@Override
587+
public boolean isExposeProxy() {
588+
return false;
589+
}
590+
591+
@Override
592+
public void setPreFiltered(boolean preFiltered) {
593+
594+
}
595+
596+
@Override
597+
public boolean isPreFiltered() {
598+
return false;
599+
}
600+
601+
@Override
602+
public Advisor[] getAdvisors() {
603+
return new Advisor[0];
604+
}
605+
606+
@Override
607+
public void addAdvisor(Advisor advisor) throws AopConfigException {
608+
609+
}
610+
611+
@Override
612+
public void addAdvisor(int pos, Advisor advisor) throws AopConfigException {
613+
614+
}
615+
616+
@Override
617+
public boolean removeAdvisor(Advisor advisor) {
618+
return false;
619+
}
620+
621+
@Override
622+
public void removeAdvisor(int index) throws AopConfigException {
623+
624+
}
625+
626+
@Override
627+
public int indexOf(Advisor advisor) {
628+
return 0;
629+
}
630+
631+
@Override
632+
public boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException {
633+
return false;
634+
}
635+
636+
@Override
637+
public void addAdvice(Advice advice) throws AopConfigException {
638+
639+
}
640+
641+
@Override
642+
public void addAdvice(int pos, Advice advice) throws AopConfigException {
643+
644+
}
645+
646+
@Override
647+
public boolean removeAdvice(Advice advice) {
648+
return false;
649+
}
650+
651+
@Override
652+
public int indexOf(Advice advice) {
653+
return 0;
654+
}
655+
656+
@Override
657+
public String toProxyConfigString() {
658+
return null;
659+
}
660+
661+
@Nullable
662+
@Override
663+
public Class<?> getTargetClass() {
664+
return null;
665+
}
666+
}
667+
508668
}

0 commit comments

Comments
 (0)