Skip to content

feat(java): detect Spring @EventListener/publishEvent() and emit HANDLES/PUBLISHES edges#591

Merged
tirth8205 merged 1 commit into
tirth8205:mainfrom
YutenC:feat/spring-event-listener-edges
Jul 17, 2026
Merged

feat(java): detect Spring @EventListener/publishEvent() and emit HANDLES/PUBLISHES edges#591
tirth8205 merged 1 commit into
tirth8205:mainfrom
YutenC:feat/spring-event-listener-edges

Conversation

@YutenC

@YutenC YutenC commented Jun 30, 2026

Copy link
Copy Markdown

Problem

Spring Application Events create implicit caller-callee relationships invisible to static analysis: a publisher calls publishEvent(new XxxEvent()) and Spring routes it to all @EventListener methods for that event type. CRG had no way to trace this path.

Solution

Two-phase resolution following the existing Spring DI / Temporal patterns:

Parse phase (parser.py):

  • @EventListener method → HANDLES edge to virtual event:XxxEvent node
  • publishEvent(new XxxEvent()) call → PUBLISHES edge to virtual event:XxxEvent node

Event type is inferred from (in priority order):

  1. @EventListener(OrderEvent.class) — explicit annotation arg
  2. @EventListener(classes = {A.class, B.class}) — multi-type named arg
  3. First method parameter type — implicit / no-arg form

Post-build resolver (event_resolver.py):

  • Joins PUBLISHES and HANDLES edges by event_type
  • Emits CALLS edges: publisher method → listener method

Usage after this change

# Find all listeners for OrderPlacedEvent
query_graph(callers_of="event:OrderPlacedEvent")

# Find what events a publisher emits
query_graph(callees_of="OrderService.placeOrder")

Changes

  • code_review_graph/parser.py — 2 constants + 3 new methods + 2 hooks in _extract_functions
  • code_review_graph/event_resolver.py — new post-build resolver
  • code_review_graph/incremental.py_run_event_resolver wired into build pipeline
  • tests/fixtures/SpringEvents.java — fixture with publisher + 2 listener patterns
  • tests/test_multilang.pyTestSpringEventListenerParsing (6 assertions)

…ES/PUBLISHES edges

Spring Application Events create implicit caller-callee relationships
that are invisible to static analysis: a publisher calls
publishEvent(new XxxEvent()) and Spring routes it to all methods
annotated @eventlistener for that event type.

This change adds two-phase resolution:

Parse phase:
  @eventlistener method  → HANDLES edge to virtual event:XxxEvent node
  publishEvent() call    → PUBLISHES edge to virtual event:XxxEvent node

Event type is inferred from:
  1. @eventlistener(OrderEvent.class) annotation argument
  2. @eventlistener(classes = {...}) named argument (multi-type)
  3. First method parameter type (implicit / no-arg form)

Post-build resolver (event_resolver.py):
  For each PUBLISHES edge, find all HANDLES edges with matching
  event_type → emit CALLS edge from publisher to listener.

Tests: TestSpringEventListenerParsing (6 assertions) in test_multilang.py
Fixture: tests/fixtures/SpringEvents.java
@tirth8205

Copy link
Copy Markdown
Owner

Thanks — Spring application events would be valuable, but the post-build resolver is not functional.

The edges table requires updated_at, while the new INSERT OR IGNORE omits it; SQLite ignores every derived CALLS row even though calls_emitted increments. Current tests inspect parser edges only and never verify a stored publisher-to-listener call.

Please write through the graph-store API or include every required column, then add full-build and incremental tests. Listener add/remove/type changes must rebuild or delete derived calls rather than permanently skipping event_resolved publishers. Match events with import/package-qualified identity so same-named events do not cross-link. Rebase and run approved current CI.

@tirth8205
tirth8205 merged commit 51bd36d into tirth8205:main Jul 17, 2026
1 check passed
tirth8205 added a commit that referenced this pull request Jul 17, 2026
Ported from PR #591 with package-qualified identities, addressable event nodes, lifecycle-safe derived calls, and event-specific queries.

Co-authored-by: YutenC <yuten.c.tcg@tc168.cloud>
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.

2 participants