Skip to content

LoggedAspect fails to start when consumer app's base package overlaps com.fayupable — @Component on LoggedAspect causes double bean registration #3

Description

@Fayupable

Summary

LoggedAspect bean creation fails at Spring context startup with:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'loggedAspect' ...
Failed to instantiate [com.fayupable.logged.spring.aspect.LoggedAspect]: No default constructor found

Caused by: java.lang.NoSuchMethodException: com.fayupable.logged.spring.aspect.LoggedAspect.()

This happens during preInstantiateSingletons, before any @Logged method is ever invoked — a pure startup-time failure.

Environment

  • logged-spring:v1.2.1 via JitPack (com.github.Fayupable.logged-lib:logged-spring:v1.2.1)
  • Spring Boot 4.0.6
  • Consumer app's base package: com.fayupable (i.e. @SpringBootApplication's implicit @componentscan root)

Root cause

LoggedAspect.java (logged-spring), lines ~109-111:

@Aspect
@Component
public class LoggedAspect {

This class carries both @aspect and @component, making it directly visible to Spring's classpath component-scanning — independently of how LoggedAutoConfiguration registers it.

It also declares two public constructors, neither annotated @Autowired:

public LoggedAspect(InvocationEventEmitter, MetricsRecorder, IClientInfoPort)
public LoggedAspect(InvocationEventEmitter, MetricsRecorder, IClientInfoPort, boolean)

Meanwhile LoggedAutoConfiguration registers the bean correctly, with real collaborators:

@Bean
@ConditionalOnMissingBean(LoggedAspect.class)
@ConditionalOnProperty(prefix = "logged", name = "enabled", havingValue = "true", matchIfMissing = true)
public LoggedAspect loggedAspect(InvocationEventEmitter eventEmitter, MetricsRecorder metricsRecorder,
                                  IClientInfoPort clientInfoPort, LoggedProperties properties) {
    return new LoggedAspect(eventEmitter, metricsRecorder, clientInfoPort, properties.getMdc().isEnabled());
}

Mechanism

  1. Any consumer app whose component-scan base package overlaps com.fayupable.logged.* (e.g. an app whose own base package is com.fayupable, or which scans broadly) will have its own @componentscan discover LoggedAspect directly, purely because it's @Component-annotated — with no knowledge of the 4-argument constructor call LoggedAutoConfiguration performs.
  2. LoggedAutoConfiguration.loggedAspect(...) is guarded by @ConditionalOnMissingBean(LoggedAspect.class). Once component-scanning has already produced a bean definition for LoggedAspect, this condition is satisfied and the auto-configuration's correct definition backs off.
  3. The component-scanned bean definition can't resolve which of the two constructors to use (no @Autowired hint) and falls back to looking for a no-arg constructor, which doesn't exist → NoSuchMethodException: ().

Confirmation this isn't consumer-specific

In a separate consumer project (also under com.fayupable), the same failure was worked around on the consumer side with:

@ComponentScan(excludeFilters = @ComponentScan.Filter(
        type = FilterType.ASSIGNABLE_TYPE,
        classes = LoggedAspect.class
))

Excluding LoggedAspect from that consumer's component-scan makes the failure disappear, since only LoggedAutoConfiguration's correct definition remains in play — confirming the trigger is component-scan visibility, not anything specific to one project's classpath.

Suggested fix

Remove @component from LoggedAspect:

@Aspect
// @Component  <- remove
public class LoggedAspect {

@aspect alone does not make a class a component-scan target — it only tells Spring AOP to proxy the bean once it exists, however it was registered. LoggedAutoConfiguration.loggedAspect(...) already constructs it correctly. Removing @component takes it out of component-scan's visibility entirely, making the auto-configuration the single, authoritative registration path — a fix independent of any consumer's package layout, so it can't recur for future consumers regardless of their base package.

Suggested follow-up (defense in depth, not the root cause)

Add @Autowired to the intended (4-arg) constructor, or remove/narrow the visibility of the 3-arg overload, so constructor selection stays unambiguous if LoggedAspect is ever instantiated some other way in the future (tests, reflection, etc.).

Verification checklist

  • A fresh Spring Boot app with base package com.fayupable (or any package overlapping com.fayupable.logged.*) starts cleanly with logged-spring on the classpath, with no @componentscan exclude filter needed on the consumer side.
  • Existing LoggedAutoConfiguration tests (ApplicationContextRunner-based) still pass.
  • mvn verify (tests + mutation testing + checkstyle) stays green.

Activity

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions