Skip to content

java: a published Spring application event reaches no listener, so @EventListener and ApplicationListener methods have no publisher #1391

Closed

Description

@swapnilpaliwal-sd

A Spring application event published with ApplicationEventPublisher.publishEvent(new T(...)) never reaches its listener, whether that is an @EventListener method taking T or an ApplicationListener<T>. path from publisher to listener says "the two are independent in this graph", and impact on the listener says "nothing resolved depends on it, so the change is local". Measured on 0.1.3 at 0df257c.

@Service
public class OrderService {
    private final ApplicationEventPublisher publisher;
    private final Audit audit;
    public OrderService(ApplicationEventPublisher publisher, Audit audit) { this.publisher = publisher; this.audit = audit; }
    public void place() { publisher.publishEvent(new OrderPlaced("1")); }
    public void ship() { publisher.publishEvent(new OrderShipped()); }
    public void placeDirect() { audit.direct(new OrderPlaced("2")); }   // control
}
@Component
public class Audit {
    @EventListener public void on(OrderPlaced e) { record(e.id); }
    public void direct(OrderPlaced e) { record(e.id); }
    void record(String s) { }
}
@Component
public class Ship implements ApplicationListener<OrderShipped> {
    @Override public void onApplicationEvent(OrderShipped e) { }
}
// OrderPlaced has a public final String id; OrderShipped is empty
query actual expected
path OrderService.place Audit.on no chain, "independent in this graph" one event hop, OrderPlaced
path OrderService.ship Ship.onApplicationEvent no chain one event hop, OrderShipped
path OrderService.placeDirect Audit.direct (control) known_edge, 1 hop same
impact Audit.on "queue entry point", then "the change is local" OrderService.place as publisher
entry_points for Ship.onApplicationEvent no row a container callback

Root cause

  • @EventListener is used only as a name: graph/java/engine/config-resolution/knobs.dl:56 lists it in cfg_listener_ann and graph/java/engine/config-resolution/entry-points.dl:69 makes it a queue entry point. Its parameter type is never read.
  • graph/java/engine/framework-behavior/destinations.dl:20-22 names "an event published to an @eventlistener" as a hop for that phase, but no rule builds it, and the send side (cfg_sends_method / cfg_sends_owner, knobs.dl:184-209) has no publishEvent entry.
  • onApplicationEvent of an ApplicationListener implementation is not registered anywhere, so it is not an entry point.

Activity

  1. swapnilpaliwal-sd commented on Sep 27, 2026

    @swapnilpaliwal-sd
    ContributorAuthor

    A second publisher form with the same result: Spring Data publishes an aggregate's domain events when the repository saves it, with no publishEvent call in client code. Measured on 0.1.3 at 0df257c; pom.xml with no dependencies, each file in src/main/java/app/ with package app; and its imports.

    @Entity
    public class Order {
        @Id Long id;
        final transient List<Object> events = new ArrayList<>();
    
        void place() { events.add(new OrderPlaced(id)); }
    
        @DomainEvents List<Object> domainEvents() { return events; }
    
        @AfterDomainEventPublication void clearEvents() { events.clear(); }
    }
    public interface OrderRepository extends JpaRepository<Order, Long> { }
    @Service
    public class OrderService {
        final OrderRepository orders;
        final OrderEvents events;
        OrderService(OrderRepository orders, OrderEvents events) { this.orders = orders; this.events = events; }
    
        void place(Order o) { o.place(); orders.save(o); }
        void placeDirect(Order o) { events.direct(new OrderPlaced(o.id)); }   // control
    }
    // OrderEvents: @EventListener void onPlaced(OrderPlaced e) and a plain void direct(OrderPlaced e)
    • axiomcode path OrderService.place OrderEvents.onPlaced .: "the two are independent in this graph".
    • axiomcode path OrderService.placeDirect OrderEvents.direct . (control): one known_edge hop.
    • axiomcode impact Order.domainEvents .: no entry point, "the change is local"; @DomainEvents and @AfterDomainEventPublication are in no knob list, so both methods have no caller.

    CLI and MCP agree. The publisher here is CrudRepository.save/saveAll/delete on a repository whose entity type has a @DomainEvents method (or extends AbstractAggregateRoot), and the event is whatever that method returns.

  2. swapnilpaliwal-sd commented on Sep 28, 2026

    @swapnilpaliwal-sd
    ContributorAuthor

    Fixed by #1630.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    engineResolution / call-graph engine rulesenhancementNew feature or requestjavaJava

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions