Class LoggedTargetGuardBeanPostProcessor

java.lang.Object
com.fayupable.logged.spring.guard.LoggedTargetGuardBeanPostProcessor
All Implemented Interfaces:
org.springframework.beans.factory.config.BeanPostProcessor

public class LoggedTargetGuardBeanPostProcessor extends Object implements org.springframework.beans.factory.config.BeanPostProcessor
Fails application startup when Logged is applied to a method that cannot be correctly instrumented: a method on a JPA entity, a Spring Data repository interface implementation, a class annotated with @Repository, or a method returning a reactive org.reactivestreams.Publisher (Reactor's Mono/Flux, or an RxJava adapter that implements the same interface).

Instrumenting an entity or repository is almost always a mistake. Entities are plain data holders whose getters can be invoked extremely frequently (for example during JSON serialization or JPA dirty checking), turning a single request into thousands of log lines and metric updates. Repositories are already covered by lower-level persistence metrics (query timing, connection pool statistics); wrapping every repository call in the same interceptor as business logic tends to blur, rather than clarify, what is slow.

A method returning a reactive Publisher cannot be correctly instrumented by this library today for a different reason: @Logged only defers its duration/outcome recording for CompletableFuture, via whenComplete. A reactive type built with Project Reactor or RxJava is lazy — nothing runs until something subscribes — so ProceedingJoinPoint#proceed() returning only means the pipeline was assembled, not that it ran. Silently recording at that point would report a near-zero duration and unconditional success regardless of what the reactive pipeline actually does once subscribed to, which is worse than not measuring it at all: it looks like real data. Rejecting it at startup is preferred over recording something misleading; full reactive support (bridging FlowContext and MDC through Reactor's Context) is a substantially larger feature, not yet implemented.

This check runs once per bean, before Spring's AOP auto-proxy creator wraps the bean, so it inspects the original target class rather than a proxy. It intentionally does not import jakarta.persistence.Entity, org.springframework.stereotype.Repository, org.springframework.data.repository.Repository, or org.reactivestreams.Publisher directly, and instead compares fully qualified names through reflection. This keeps this module free of a hard dependency on JPA, Spring Data, or a reactive streams implementation, so consumers who use none of these are not forced to bring them onto the classpath.

  • Constructor Details

    • LoggedTargetGuardBeanPostProcessor

      public LoggedTargetGuardBeanPostProcessor()
  • Method Details

    • postProcessBeforeInitialization

      public Object postProcessBeforeInitialization(Object bean, @NonNull String beanName) throws org.springframework.beans.BeansException
      Specified by:
      postProcessBeforeInitialization in interface org.springframework.beans.factory.config.BeanPostProcessor
      Throws:
      org.springframework.beans.BeansException