Skip to content

Commit d6e5288

Browse files
committed
Refine exclusions for AOT processing.
Throw MappingException referring to the entity type for easier debugging of the property creation chain. Add exclusion for properties starting with $$_ (synthetic Hibernate fields). Exclude java.util and javax types from AOT entity processing as well as arrays. See #2595 Original Pull Request: #3318
1 parent 947f9c4 commit d6e5288

File tree

6 files changed

+124
-32
lines changed

6 files changed

+124
-32
lines changed

src/main/java/org/springframework/data/aot/AotMappingContext.java

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,14 @@
1515
*/
1616
package org.springframework.data.aot;
1717

18-
import java.lang.reflect.InaccessibleObjectException;
19-
20-
import org.apache.commons.logging.Log;
21-
import org.apache.commons.logging.LogFactory;
18+
import java.math.BigDecimal;
19+
import java.text.Format;
20+
import java.time.LocalDateTime;
21+
import java.util.UUID;
22+
import java.util.function.Predicate;
23+
24+
import org.springframework.data.domain.Page;
25+
import org.springframework.data.geo.Point;
2226
import org.springframework.data.mapping.Association;
2327
import org.springframework.data.mapping.PersistentEntity;
2428
import org.springframework.data.mapping.context.AbstractMappingContext;
@@ -41,8 +45,6 @@
4145
class AotMappingContext extends
4246
AbstractMappingContext<BasicPersistentEntity<?, AotMappingContext.AotPersistentProperty>, AotMappingContext.AotPersistentProperty> {
4347

44-
private static final Log logger = LogFactory.getLog(AotMappingContext.class);
45-
4648
private final EntityInstantiators instantiators = new EntityInstantiators();
4749
private final AotAccessorFactory propertyAccessorFactory = new AotAccessorFactory();
4850

@@ -55,25 +57,43 @@ class AotMappingContext extends
5557
*/
5658
public void contribute(Class<?> entityType) {
5759

58-
try {
59-
BasicPersistentEntity<?, AotPersistentProperty> entity = getPersistentEntity(entityType);
60-
61-
if (entity != null && !entity.getType().isArray()) {
60+
BasicPersistentEntity<?, AotPersistentProperty> entity = getPersistentEntity(entityType);
6261

63-
EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity);
64-
if (instantiator instanceof EntityInstantiatorSource source) {
65-
source.getInstantiatorFor(entity);
66-
}
62+
if (entity != null) {
6763

68-
propertyAccessorFactory.initialize(entity);
69-
}
70-
} catch (InaccessibleObjectException exception) {
71-
if(logger.isInfoEnabled()) {
72-
logger.info("Unable to contribute bytecode accessor for [%s]".formatted(entityType), exception);
64+
EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity);
65+
if (instantiator instanceof EntityInstantiatorSource source) {
66+
source.getInstantiatorFor(entity);
7367
}
68+
69+
propertyAccessorFactory.initialize(entity);
7470
}
7571
}
7672

73+
@Override
74+
protected boolean shouldCreatePersistentEntityFor(TypeInformation<?> typeInformation) {
75+
76+
Class<?> type = typeInformation.getType();
77+
78+
if (type.isArray() || type.isPrimitive() || type.isEnum()) {
79+
return false;
80+
}
81+
82+
Predicate<Class<?>> isInPackage = packageMember -> type.getPackageName().startsWith(packageMember.getPackageName());
83+
84+
if (isInPackage.test(UUID.class) // java.util
85+
|| isInPackage.test(BigDecimal.class) // java.math
86+
|| isInPackage.test(LocalDateTime.class) // java.time
87+
|| isInPackage.test(Format.class) // java.text
88+
|| isInPackage.test(Point.class) // org.springframework.data.geo
89+
|| isInPackage.test(Page.class) // org.springframework.data.domain
90+
|| type.getPackageName().startsWith("javax")) {
91+
return false;
92+
}
93+
94+
return super.shouldCreatePersistentEntityFor(typeInformation);
95+
}
96+
7797
@Override
7898
protected <T> BasicPersistentEntity<?, AotPersistentProperty> createPersistentEntity(
7999
TypeInformation<T> typeInformation) {

src/main/java/org/springframework/data/aot/AotTypeConfiguration.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,15 @@
3535
* cglib bytecode generation and other common tasks.
3636
* <p>
3737
* On {@link #contribute(Environment, GenerationContext)} the configuration is added to the {@link GenerationContext}.
38-
*
38+
*
3939
* @author Christoph Strobl
4040
* @since 4.0
4141
*/
4242
public interface AotTypeConfiguration {
4343

4444
/**
4545
* Configure the referenced type for data binding. In case of {@link java.lang.annotation.Annotation} only data ones
46-
* are considered. For more fine grained control use {@link #forReflectiveAccess(MemberCategory...)}.
46+
* are considered. For more fine-grained control use {@link #forReflectiveAccess(MemberCategory...)}.
4747
*
4848
* @return this.
4949
*/
@@ -71,7 +71,7 @@ public interface AotTypeConfiguration {
7171
* Configure the referenced type as a projection interface returned by eg. a query method.
7272
* <p>
7373
* Shortcut for {@link #proxyInterface(Class[]) proxyInterface(TargetAware, SpringProxy, DecoratingProxy)}
74-
*
74+
*
7575
* @return this.
7676
*/
7777
default AotTypeConfiguration usedAsProjectionInterface() {
@@ -111,7 +111,7 @@ default AotTypeConfiguration repositoryProxy() {
111111

112112
/**
113113
* Register a proxy for the referenced type that also implements the given proxyInterfaces.
114-
*
114+
*
115115
* @param proxyInterfaces additional interfaces the proxy implements. Order matters!
116116
* @return this.
117117
*/
@@ -129,14 +129,14 @@ default AotTypeConfiguration proxyInterface(Class<?>... proxyInterfaces) {
129129

130130
/**
131131
* Configure the referenced type for usage with Querydsl by registering hints for potential {@code Q} types.
132-
*
132+
*
133133
* @return this.
134134
*/
135135
AotTypeConfiguration forQuerydsl();
136136

137137
/**
138138
* Write the configuration to the given {@link GenerationContext}.
139-
*
139+
*
140140
* @param environment must not be {@literal null}.
141141
* @param generationContext must not be {@literal null}.
142142
*/

src/main/java/org/springframework/data/aot/DefaultAotContext.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.springframework.aot.hint.TypeReference;
3636
import org.springframework.beans.factory.BeanFactory;
3737
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
38+
import org.springframework.beans.factory.aot.AotProcessingException;
3839
import org.springframework.beans.factory.config.BeanDefinition;
3940
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
4041
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
@@ -229,6 +230,16 @@ public AotTypeConfiguration forQuerydsl() {
229230
@Override
230231
public void contribute(Environment environment, GenerationContext generationContext) {
231232

233+
try {
234+
doContribute(environment, generationContext);
235+
} catch (RuntimeException e) {
236+
throw new AotProcessingException("Cannot contribute Ahead-of-Time optimizations for '" + type.getName() + "'",
237+
e);
238+
}
239+
}
240+
241+
private void doContribute(Environment environment, GenerationContext generationContext) {
242+
232243
if (!this.categories.isEmpty()) {
233244
generationContext.getRuntimeHints().reflection().registerType(this.type,
234245
categories.toArray(MemberCategory[]::new));

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

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -424,8 +424,9 @@ protected Optional<E> addPersistentEntity(TypeInformation<?> typeInformation) {
424424

425425
entity = doAddPersistentEntity(typeInformation);
426426

427-
} catch (BeansException e) {
428-
throw new MappingException(e.getMessage(), e);
427+
} catch (RuntimeException e) {
428+
throw new MappingException(
429+
"Cannot create PersistentEntity for '%s'".formatted(typeInformation.getType().getName()), e);
429430
} finally {
430431
write.unlock();
431432
}
@@ -763,17 +764,18 @@ enum PersistentPropertyFilter implements FieldFilter {
763764

764765
INSTANCE;
765766

766-
private static final Streamable<PropertyMatch> UNMAPPED_PROPERTIES;
767+
private static final Collection<PropertyMatch> UNMAPPED_PROPERTIES;
767768

768769
static {
769770

770771
Set<PropertyMatch> matches = new HashSet<>();
771772
matches.add(new PropertyMatch("class", null));
772773
matches.add(new PropertyMatch("this\\$.*", null));
774+
matches.add(new PropertyMatch("\\$\\$_.*", null));
773775
matches.add(new PropertyMatch("metaClass", "groovy.lang.MetaClass"));
774776
matches.add(new KotlinDataClassPropertyMatch(".*\\$delegate", null));
775777

776-
UNMAPPED_PROPERTIES = Streamable.of(matches);
778+
UNMAPPED_PROPERTIES = matches;
777779
}
778780

779781
@Override
@@ -783,8 +785,13 @@ public boolean matches(Field field) {
783785
return false;
784786
}
785787

786-
return UNMAPPED_PROPERTIES.stream()//
787-
.noneMatch(it -> it.matches(field));
788+
for (PropertyMatch property : UNMAPPED_PROPERTIES) {
789+
if (property.matches(field)) {
790+
return false;
791+
}
792+
}
793+
794+
return true;
788795
}
789796

790797
/**

src/test/java/org/springframework/data/aot/AotMappingContextUnitTests.java

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,43 @@
1515
*/
1616
package org.springframework.data.aot;
1717

18+
import static org.assertj.core.api.Assertions.*;
19+
20+
import java.util.BitSet;
21+
1822
import org.junit.jupiter.api.Test;
23+
1924
import org.springframework.data.annotation.Id;
2025
import org.springframework.data.annotation.Reference;
2126
import org.springframework.data.util.TypeInformation;
2227

2328
/**
29+
* Unit tests for {@link AotMappingContext}.
30+
*
2431
* @author Christoph Strobl
32+
* @author Mark Paluch
2533
*/
2634
public class AotMappingContextUnitTests {
2735

36+
AotMappingContext context = new AotMappingContext();
37+
2838
@Test // GH-2595
2939
void obtainEntityWithReference() {
30-
new AotMappingContext().getPersistentEntity(TypeInformation.of(DemoEntity.class));
40+
context.getPersistentEntity(TypeInformation.of(DemoEntity.class));
41+
}
42+
43+
@Test // GH-2595
44+
void doesNotCreateEntityForBitSet() {
45+
46+
assertThat(context.getPersistentEntity(EntityWithBitSet.class)).isNotNull();
47+
assertThat(context.getPersistentEntity(BitSet.class)).isNull();
48+
}
49+
50+
@Test // GH-2595
51+
void doesNotCreateEntityForJavaxReference() {
52+
53+
assertThat(context.getPersistentEntity(EntityWithReference.class)).isNotNull();
54+
assertThat(context.getPersistentEntity(javax.naming.Reference.class)).isNull();
3155
}
3256

3357
static class DemoEntity {
@@ -41,4 +65,17 @@ static class DemoEntity {
4165
static class ReferencedEntity {
4266
@Id String id;
4367
}
68+
69+
static class EntityWithBitSet {
70+
71+
@Id String id;
72+
BitSet bitset;
73+
}
74+
75+
static class EntityWithReference {
76+
77+
@Id String id;
78+
javax.naming.Reference reference;
79+
}
80+
4481
}

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,18 @@ void doesNotCreatePersistentPropertyForGroovyMetaClass() {
118118
assertThat(entity.getPersistentProperty("metaClass")).isNull();
119119
}
120120

121+
@Test // GH-2595
122+
void doesNotCreatePersistentPropertyHibernateBytecodeEnhancements() {
123+
124+
var mappingContext = new SampleMappingContext();
125+
mappingContext.initialize();
126+
127+
PersistentEntity<Object, SamplePersistentProperty> entity = mappingContext
128+
.getRequiredPersistentEntity(Sample.class);
129+
assertThat(entity.getPersistentProperty("$$_hibernate")).isNull();
130+
assertThat(mappingContext.hasPersistentEntityFor(Excluded.class)).isFalse();
131+
}
132+
121133
@Test // DATACMNS-332
122134
void usesMostConcreteProperty() {
123135

@@ -401,10 +413,15 @@ private class Unsupported {
401413
class Sample {
402414

403415
MetaClass metaClass;
416+
Excluded $$_hibernate;
404417
List<Person> persons;
405418
TreeMap<String, Person> personMap;
406419
}
407420

421+
class Excluded {
422+
423+
}
424+
408425
static class Base {
409426
String foo;
410427
}

0 commit comments

Comments
 (0)