3030import org .springframework .data .mapping .model .PreferredConstructorDiscoverer ;
3131import org .springframework .data .projection .ProjectionFactory ;
3232import org .springframework .data .projection .ProjectionInformation ;
33+ import org .springframework .data .util .Lazy ;
34+ import org .springframework .lang .Contract ;
3335import org .springframework .lang .NonNull ;
3436import org .springframework .lang .Nullable ;
3537import org .springframework .util .Assert ;
@@ -82,7 +84,7 @@ public static ReturnedType of(Class<?> returnedType, Class<?> domainType, Projec
8284 }
8385
8486 /**
85- * Returns the entity type.
87+ * Return the entity type.
8688 *
8789 * @return
8890 */
@@ -91,74 +93,89 @@ public final Class<?> getDomainType() {
9193 }
9294
9395 /**
94- * Returns whether the given source object is an instance of the returned type.
96+ * Return whether the given source object is an instance of the returned type.
9597 *
9698 * @param source can be {@literal null}.
9799 * @return
98100 */
101+ @ Contract ("null -> false" )
99102 public final boolean isInstance (@ Nullable Object source ) {
100103 return getReturnedType ().isInstance (source );
101104 }
102105
103106 /**
104- * Returns whether the type is projecting, i.e. not of the domain type .
107+ * Return the type of the individual objects to return .
105108 *
106109 * @return
107110 */
108- public abstract boolean isProjecting ();
111+ public abstract Class <?> getReturnedType ();
109112
110113 /**
111- * Returns the type of the individual objects to return .
114+ * Return whether the type is projecting, i.e. not of the domain type .
112115 *
113116 * @return
114117 */
115- public abstract Class <?> getReturnedType ();
118+ public abstract boolean isProjecting ();
116119
117120 /**
118- * Returns whether the returned type will require custom construction .
121+ * Return whether the type is an interface-projection .
119122 *
120- * @return
123+ * @since 3.5.7
121124 */
122- public abstract boolean needsCustomConstruction ();
125+ public boolean isInterfaceProjection () {
126+ return isProjecting () && getReturnedType ().isInterface ();
127+ }
123128
124129 /**
125- * Returns the type that the query execution is supposed to pass to the underlying infrastructure. {@literal null} is
126- * returned to indicate a generic type (a map or tuple-like type) shall be used.
130+ * Return whether the type is a DTO projection.
127131 *
128- * @return
132+ * @since 3.5.7
129133 */
130- @ Nullable
131- public abstract Class <?> getTypeToRead ();
134+ public boolean isDtoProjection () {
135+ return isProjecting () && !getReturnedType ().isInterface ();
136+ }
132137
133138 /**
134- * Returns the properties required to be used to populate the result.
139+ * Return the properties required to be used to populate the result.
135140 *
136- * @return
137141 * @see ProjectionInformation#getInputProperties()
138142 */
139143 public abstract List <String > getInputProperties ();
140144
141145 /**
142- * Returns whether the returned type has input properties.
146+ * Return whether the returned type has input properties.
143147 *
144- * @return
145148 * @since 3.3.5
146149 * @see ProjectionInformation#hasInputProperties()
147150 */
148151 public boolean hasInputProperties () {
149152 return !CollectionUtils .isEmpty (getInputProperties ());
150153 }
151154
155+ /**
156+ * Return whether the returned type will require custom construction.
157+ */
158+ public abstract boolean needsCustomConstruction ();
159+
160+ /**
161+ * Return the type that the query execution is supposed to pass to the underlying infrastructure. {@literal null} is
162+ * returned to indicate a generic type (a map or tuple-like type) shall be used.
163+ */
164+ @ Nullable
165+ public abstract Class <?> getTypeToRead ();
166+
152167 /**
153168 * A {@link ReturnedType} that's backed by an interface.
154169 *
155170 * @author Oliver Gierke
171+ * @author Mark Paluch
156172 * @since 1.12
157173 */
158174 private static final class ReturnedInterface extends ReturnedType {
159175
160176 private final ProjectionInformation information ;
161177 private final Class <?> domainType ;
178+ private final boolean isProjecting ;
162179 private final List <String > inputProperties ;
163180
164181 /**
@@ -175,6 +192,7 @@ public ReturnedInterface(ProjectionInformation information, Class<?> domainType)
175192
176193 this .information = information ;
177194 this .domainType = domainType ;
195+ this .isProjecting = !information .getType ().isAssignableFrom (domainType );
178196 this .inputProperties = detectInputProperties (information );
179197 }
180198
@@ -197,32 +215,44 @@ public Class<?> getReturnedType() {
197215 }
198216
199217 @ Override
200- public boolean needsCustomConstruction () {
201- return isProjecting () && information . isClosed () ;
218+ public boolean isProjecting () {
219+ return isProjecting ;
202220 }
203221
204222 @ Override
205- public boolean isProjecting () {
206- return ! information . getType (). isAssignableFrom ( domainType );
223+ public boolean isInterfaceProjection () {
224+ return isProjecting ( );
207225 }
208226
209- @ Nullable
210227 @ Override
211- public Class <?> getTypeToRead () {
212- return isProjecting () && information . isClosed () ? null : domainType ;
228+ public boolean isDtoProjection () {
229+ return false ;
213230 }
214231
215232 @ Override
216233 public List <String > getInputProperties () {
217234 return inputProperties ;
218235 }
236+
237+ @ Override
238+ public boolean needsCustomConstruction () {
239+ return isProjecting () && information .isClosed ();
240+ }
241+
242+ @ Override
243+ @ Nullable
244+ public Class <?> getTypeToRead () {
245+ return isProjecting () && information .isClosed () ? null : domainType ;
246+ }
247+
219248 }
220249
221250 /**
222251 * A {@link ReturnedType} that's backed by an actual class.
223252 *
224253 * @author Oliver Gierke
225254 * @author Mikhail Polivakha
255+ * @author Mark Paluch
226256 * @since 1.12
227257 */
228258 private static final class ReturnedClass extends ReturnedType {
@@ -231,7 +261,8 @@ private static final class ReturnedClass extends ReturnedType {
231261
232262 private final Class <?> type ;
233263 private final boolean isDto ;
234- private final List <String > inputProperties ;
264+ private final @ Nullable PreferredConstructor <?, ?> constructor ;
265+ private final Lazy <List <String >> inputProperties ;
235266
236267 /**
237268 * Creates a new {@link ReturnedClass} instance for the given returned type and domain type.
@@ -256,7 +287,13 @@ public ReturnedClass(Class<?> returnedType, Class<?> domainType) {
256287 !VOID_TYPES .contains (type ) && //
257288 !type .getPackage ().getName ().startsWith ("java." );
258289
259- this .inputProperties = detectConstructorParameterNames (returnedType );
290+ this .constructor = detectConstructorParameterNames (type );
291+
292+ if (this .constructor == null ) {
293+ this .inputProperties = Lazy .of (Collections .emptyList ());
294+ } else {
295+ this .inputProperties = Lazy .of (this ::detectConstructorParameterNames );
296+ }
260297 }
261298
262299 @ Override
@@ -265,33 +302,55 @@ public Class<?> getReturnedType() {
265302 }
266303
267304 @ Override
268- @ NonNull
269- public Class <?> getTypeToRead () {
270- return type ;
305+ public boolean isProjecting () {
306+ return isDto ;
271307 }
272308
273309 @ Override
274- public boolean isProjecting () {
275- return isDto () ;
310+ public boolean isInterfaceProjection () {
311+ return false ;
276312 }
277313
278314 @ Override
279- public boolean needsCustomConstruction () {
280- return isDto () && ! inputProperties . isEmpty ();
315+ public boolean isDtoProjection () {
316+ return isProjecting ();
281317 }
282318
283319 @ Override
284320 public List <String > getInputProperties () {
285- return inputProperties ;
321+ return inputProperties . get () ;
286322 }
287323
288- private List <String > detectConstructorParameterNames (Class <?> type ) {
324+ @ Override
325+ public boolean hasInputProperties () {
326+ return this .constructor != null && this .constructor .getParameterCount () > 0 && super .hasInputProperties ();
327+ }
289328
290- if (!isDto ()) {
291- return Collections .emptyList ();
292- }
329+ @ Override
330+ public boolean needsCustomConstruction () {
331+ return isDtoProjection () && hasInputProperties ();
332+ }
293333
294- PreferredConstructor <?, ?> constructor = PreferredConstructorDiscoverer .discover (type );
334+ @ Override
335+ @ NonNull
336+ public Class <?> getTypeToRead () {
337+ return type ;
338+ }
339+
340+ private boolean isDomainSubtype () {
341+ return getDomainType ().equals (type ) && getDomainType ().isAssignableFrom (type );
342+ }
343+
344+ private boolean isPrimitiveOrWrapper () {
345+ return ClassUtils .isPrimitiveOrWrapper (type );
346+ }
347+
348+ @ Nullable
349+ private PreferredConstructor <?, ?> detectConstructorParameterNames (Class <?> type ) {
350+ return isDtoProjection () ? PreferredConstructorDiscoverer .discover (type ) : null ;
351+ }
352+
353+ private List <String > detectConstructorParameterNames () {
295354
296355 if (constructor == null ) {
297356 return Collections .emptyList ();
@@ -310,24 +369,13 @@ private List<String> detectConstructorParameterNames(Class<?> type) {
310369 if (logger .isWarnEnabled ()) {
311370 logger .warn (("No constructor parameter names discovered. "
312371 + "Compile the affected code with '-parameters' instead or avoid its introspection: %s" )
313- .formatted (type .getName ()));
372+ .formatted (constructor . getConstructor (). getDeclaringClass () .getName ()));
314373 }
315374 }
316375
317376 return Collections .unmodifiableList (properties );
318377 }
319378
320- private boolean isDto () {
321- return isDto ;
322- }
323-
324- private boolean isDomainSubtype () {
325- return getDomainType ().equals (type ) && getDomainType ().isAssignableFrom (type );
326- }
327-
328- private boolean isPrimitiveOrWrapper () {
329- return ClassUtils .isPrimitiveOrWrapper (type );
330- }
331379 }
332380
333381 private static final class CacheKey {
@@ -394,5 +442,7 @@ public String toString() {
394442 return "ReturnedType.CacheKey(returnedType=" + this .getReturnedType () + ", domainType=" + this .getDomainType ()
395443 + ", projectionFactoryHashCode=" + this .getProjectionFactoryHashCode () + ")" ;
396444 }
445+
397446 }
447+
398448}
0 commit comments