Repository navigation
Warn at build when a resolver can't be called on its JDK dynamic proxy - #853
Open
oryan-block wants to merge 9 commits into
Open
oryan-block wants to merge 9 commits into
oryan-block wants to merge 9 commits into
Conversation
Resolvers behind a JDK dynamic proxy, e.g. Spring AOP without proxyTargetClass, are scanned as their target class, but the proxy only implements the target's interfaces, so every field failed at runtime with "object of type jdk.proxy2.$ProxyN is not an instance of ...". Each resolver method is now called through the interface method it implements, so the proxy's interceptors still run, and build() fails with a clear error if no interface declares it. Static methods are still called directly. Only the first matching resolver of a non-root field is checked, so a supertype resolver's method that a more specific resolver takes precedence over doesn't fail the build. Fixes #822 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A resolver method inherited from a generic superclass, e.g. getValue(T) of FooResolver<T>, is erased to getValue(Object), so it didn't match the interface method it implements and build() threw the new FieldResolverError for a supported resolver pattern. Its parameter types are now resolved against the target class too, like the interface's. Also covers the guards that skip interfaces the proxy doesn't implement and static interface methods. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lver method Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the proxy's interfaces Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Fixes #822
Checklist
Description
A resolver behind a JDK dynamic proxy, like Spring AOP without
proxyTargetClass, builds fine but fails on every field at query time. With the repro from the issue,{ test }returnsdata={test=null}with:Spring4AopProxyHandlermakes the scanner match fields against the target class, but the proxy only implements the target's interfaces. CGLIB proxies work because they subclass the target.The earlier commits here called the methods through the proxy's interface methods, so interceptors would still run. I dropped that:
spring.aop.proxy-target-class=true)Now
SchemaClassScanner#warnIfNotCallableOnResolverchecks the resolver picked for each field. If the resolver isn't an instance of the class that declares the method or field, and the member isn't static, it logs a warning and suggests class-based proxies:It covers public fields too, so
PropertyFieldResolver.propertyis now aval. For a field it also suggests adding a getter, since class-based proxies don't pass field reads to the target. A CGLIB proxy returned0for@JvmField val count = 1. Only the resolver that was picked gets checked, so a supertype resolver that a more specific resolver shadows doesn't warn. CGLIB, Guice and similar proxies subclass the target, so they don't warn either. Nothing else changes, and the field still fails at query time like before. Throwing instead has to wait for 15.0.0: #868.No unit test checks the warning. The tests run on the module path and
graphql.java.toolscan't read logback, same as the warnings from #855.SchemaParserTest."parser handles spring AOP proxied resolvers by default"is back unchanged and still builds.mvn clean verifypasses (317 tests), also with-Dgraphql-java.version=25.1.I checked it through the installed jar in a separate consumer project with
type Query { test: String, count: Int }, a resolver withfun test() = "ok"and@JvmField val count = 1, and{ test count }:Query.test,Query.count), and the query fails like before. On master it fails the same way, with no warning.testreturns"ok"and a getter (open val total = 2) returns2, but the fieldcountreturns0{"test": "ok", "count": 1}Release note: Resolvers behind a JDK dynamic proxy now log a warning at build when a field's method or field can't be called on the proxy. Use class-based proxies (
proxyTargetClass = true).