Skip to content

fix(apt): remove incorrect 'new QClass()' suggestion from circular Q-class warning - #1905

Open
o54711254 wants to merge 1 commit into
OpenFeign:masterfrom
o54711254:fix/circular-warning-remove-new-suggestion
Open

fix(apt): remove incorrect 'new QClass()' suggestion from circular Q-class warning#1905
o54711254 wants to merge 1 commit into
OpenFeign:masterfrom
o54711254:fix/circular-warning-remove-new-suggestion

Conversation

@o54711254

Copy link
Copy Markdown
Contributor

Background

In #1739 I added compile-time detection for circular Q-class references that can
cause class initialization deadlocks. The warning message suggests three
workarounds:

To avoid deadlock, consider:
  (1) Removing the bidirectional association on one side.
  (2) Pre-initializing Q-classes in a single thread before handling requests (e.g. via @PostConstruct).
  (3) Using 'new QClass("alias")' instead of static field access in your repositories.

Suggestion (3) does not work. This PR removes it.

Why (3) does not work

Creating an instance of a class triggers class initialization (JLS 12.4.1),
exactly as accessing a static field does. So new QOrder("alias") still runs
QOrder.<clinit>, which initializes the static field, whose constructor
creates the other Q-class — the same chain that causes the deadlock.

Given a bidirectional @OneToOne between Order and Payment, the generated
class looks like this:

public class QOrder extends EntityPathBase<Order> {
    private static final PathInits INITS = PathInits.DIRECT2;
    public static final QOrder order = new QOrder("order");

    public final QPayment payment;

    public QOrder(Class<? extends Order> type, PathMetadata metadata, PathInits inits) {
        super(type, metadata, inits);
        this.payment = inits.isInitialized("payment")
            ? new QPayment(forProperty("payment"), inits.get("payment"))
            : null;
    }
}

new QOrder("alias")
→ triggers QOrder.<clinit>
→ initializes the static field order
→ its constructor creates QPayment
→ triggers QPayment.<clinit>

Avoiding static field access does not avoid static field initialization.

Reproduction

Two threads, each entering only via new:

CountDownLatch start = new CountDownLatch(1);

Thread a = new Thread(() -> {
    awaitQuietly(start);
    new QOrder("a");
    System.out.println("A done");
});
Thread b = new Thread(() -> {
    awaitQuietly(start);
    new QPayment("b");
    System.out.println("B done");
});

a.start();
b.start();
start.countDown();

a.join(5000);
b.join(5000);

System.out.println("A alive: " + a.isAlive());
System.out.println("B alive: " + b.isAlive());

Output:

A alive: true
B alive: true

Neither thread completed — both were blocked on class initialization monitors.

Change

Removes suggestion (3) from the warning message. (1) and (2) remain valid and
are unaffected. The detection logic itself is unchanged.

Follow-up to #1739. Sorry for the incorrect guidance in the original PR.

…class warning

Suggestion (3) in the circular Q-class warning claimed that using
'new QClass("alias")' would avoid the initialization deadlock, but
instance creation triggers class initialization (JLS 12.4.1) just like
static field access does, so the same deadlock occurs.

Follow-up to OpenFeign#1739.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant