CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-springframework--spring-aop

Spring AOP module providing aspect-oriented programming capabilities for the Spring Framework

Pending
Overview
Eval results
Files

advice-interceptors.mddocs/

Advice Types and Interceptors

Implementation of different advice types (before, after, around, throws) and method interceptors for cross-cutting concerns like performance monitoring, debugging, concurrency control, and asynchronous execution. These components provide the actual behavior that gets executed at join points identified by pointcuts.

Capabilities

AOP Alliance Method Interceptors

The foundational interceptor interface from AOP Alliance that all Spring interceptors implement or extend.

public interface MethodInterceptor extends Interceptor {
    /**
     * Implement this method to perform extra treatments before and
     * after the invocation. Polite implementations would certainly
     * like to invoke {@link Joinpoint#proceed()}.
     * @param invocation the method invocation joinpoint
     * @return the result of the call to {@link Joinpoint#proceed()};
     * might be intercepted by the interceptor
     * @throws Throwable if the interceptors or the target object
     * throws an exception
     */
    Object invoke(MethodInvocation invocation) throws Throwable;
}

public interface MethodInvocation extends Invocation {
    /**
     * Get the method being invoked.
     * <p>This method is a friendly implementation of the
     * {@link Joinpoint#getStaticPart()} method (same result).
     * @return the method being invoked
     */
    Method getMethod();
}

Performance and Debugging Interceptors

Interceptors for monitoring method execution performance and debugging method calls.

public class PerformanceMonitorInterceptor extends AbstractMonitoringInterceptor {
    /**
     * Create a new PerformanceMonitorInterceptor with a static logger.
     */
    public PerformanceMonitorInterceptor();
    
    /**
     * Create a new PerformanceMonitorInterceptor with a dynamic or static logger,
     * according to the given flag.
     * @param useDynamicLogger whether to use a dynamic logger or a static logger
     * @see #setUseDynamicLogger
     */
    public PerformanceMonitorInterceptor(boolean useDynamicLogger);
    
    @Override
    protected Object invokeUnderTrace(MethodInvocation invocation, Log logger) throws Throwable;
    
    /**
     * Return a description for the given method invocation.
     * @param invocation the invocation to describe
     * @return the description to use
     */
    protected String getDescriptionString(MethodInvocation invocation);
}

public class SimpleTraceInterceptor extends AbstractTraceInterceptor {
    /**
     * Create a new SimpleTraceInterceptor with a static logger.
     */
    public SimpleTraceInterceptor();
    
    /**
     * Create a new SimpleTraceInterceptor with dynamic or static logger,
     * according to the given flag.
     * @param useDynamicLogger whether to use a dynamic logger or a static logger
     * @see #setUseDynamicLogger
     */
    public SimpleTraceInterceptor(boolean useDynamicLogger);
    
    @Override
    protected Object invokeUnderTrace(MethodInvocation invocation, Log logger) throws Throwable;
    
    /**
     * Return a description for the given method invocation.
     * @param invocation the invocation to describe
     * @return the description
     */
    protected String getDescriptionString(MethodInvocation invocation);
}

public class CustomizableTraceInterceptor extends AbstractTraceInterceptor {
    /**
     * Set the template to use for method entry log messages.
     * This template can contain any of the following placeholders:
     * <ul>
     * <li>{@code $[methodName]} - replaced with the name of the method being invoked</li>
     * <li>{@code $[targetClassName]} - replaced with the name of the class that is
     * the target of the invocation</li>
     * <li>{@code $[targetClassShortName]} - replaced with the short name of the class
     * that is the target of the invocation</li>
     * <li>{@code $[returnValue]} - replaced with the value returned by the invocation</li>
     * <li>{@code $[argumentTypes]} - replaced with a comma-separated list of the
     * argument types for the method</li>
     * <li>{@code $[arguments]} - replaced with a comma-separated list of the
     * String representation of the method arguments</li>
     * <li>{@code $[exception]} - replaced with the {@code String} representation
     * of any {@link Throwable} raised during the invocation</li>
     * <li>{@code $[invocationTime]} - replaced with the time, in milliseconds,
     * taken by the method invocation</li>
     * </ul>
     * @param enterMessage the template to use for method entry log messages
     */
    public void setEnterMessage(String enterMessage);
    
    /**
     * Set the template to use for method exit log messages.
     * @param exitMessage the template to use for method exit log messages
     * @see #setEnterMessage
     */
    public void setExitMessage(String exitMessage);
    
    /**
     * Set the template to use for method exception log messages.
     * @param exceptionMessage the template to use for method exception log messages
     * @see #setEnterMessage
     */
    public void setExceptionMessage(String exceptionMessage);
    
    @Override
    protected Object invokeUnderTrace(MethodInvocation invocation, Log logger) throws Throwable;
    
    /**
     * Replace the placeholders in the given message template with the
     * supplied values, or values derived from those supplied.
     * @param message the message template containing the placeholders to be replaced
     * @param methodInvocation the {@code MethodInvocation} being logged.
     * Used to derive values for all placeholders except {@code $[exception]}
     * and {@code $[invocationTime]}.
     * @param ex the {@code Throwable} returned by the method invocation. Used to
     * derive the {@code $[exception]} placeholder. Can be {@code null}.
     * @param invocationTime the value to write in place of the
     * {@code $[invocationTime]} placeholder
     * @return the formatted output to write to the log
     */
    protected String replacePlaceholders(String message, MethodInvocation methodInvocation,
                                       Object returnValue, Throwable ex, long invocationTime);
}

public class DebugInterceptor extends SimpleTraceInterceptor {
    /**
     * Create a new DebugInterceptor with a static logger.
     */
    public DebugInterceptor();
    
    /**
     * Create a new DebugInterceptor with dynamic or static logger,
     * according to the given flag.
     * @param useDynamicLogger whether to use a dynamic logger or a static logger
     * @see #setUseDynamicLogger
     */
    public DebugInterceptor(boolean useDynamicLogger);
    
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable;
    
    /**
     * Return the number of times this interceptor has been invoked.
     * @return the invocation count
     */
    public long getCount();
    
    /**
     * Reset the invocation count to zero.
     */
    public synchronized void resetCount();
}

Base Monitoring Interceptor

Abstract base class for interceptors that monitor method execution.

public abstract class AbstractMonitoringInterceptor implements MethodInterceptor, Serializable {
    /**
     * Set the name of the logger to use. The name will be passed to the
     * underlying logger implementation through Commons Logging, getting
     * interpreted as log category according to the logger's configuration.
     * <p>This can be specified to not log into the category of a class
     * (whether this interceptor's class or the class getting called)
     * but rather into a specific named category.
     * <p><b>NOTE:</b> Specify either this property or "useDynamicLogger", not both.
     * @param loggerName the name of the logger
     * @see #setUseDynamicLogger
     */
    public void setLoggerName(String loggerName);
    
    /**
     * Set whether to use a dynamic logger or a static logger.
     * Default is a static logger for this trace interceptor.
     * <p>Used to determine which {@code Log} instance should be used to write
     * log messages for a particular method invocation: a dynamic one for the
     * {@code Class} getting called, or a static one for the {@code Class}
     * of the trace interceptor.
     * <p><b>NOTE:</b> Specify either this property or "loggerName", not both.
     * @param useDynamicLogger {@code true} if the logger should be dynamic;
     * {@code false} for a static logger
     * @see #setLoggerName
     */
    public void setUseDynamicLogger(boolean useDynamicLogger);
    
    /**
     * Return whether to use a dynamic logger or a static logger.
     */
    public boolean isUseDynamicLogger();
    
    /**
     * Set whether to log at TRACE level. Default is DEBUG level.
     * @param logAtTrace {@code true} to log at TRACE;
     * {@code false} to log at DEBUG
     */
    public void setLogAtTrace(boolean logAtTrace);
    
    /**
     * Return whether to log at TRACE level.
     */
    public boolean isLogAtTrace();
    
    /**
     * Determine the appropriate {@code Log} instance to use for the given
     * {@code MethodInvocation}. If the {@code useDynamicLogger} flag is set,
     * the {@code Log} instance will be for the target class of the
     * {@code MethodInvocation}, otherwise the {@code Log} will be the
     * default static logger.
     * @param invocation the {@code MethodInvocation} being traced
     * @return the {@code Log} instance to use
     * @see #setUseDynamicLogger
     */
    protected Log getLoggerForInvocation(MethodInvocation invocation);
    
    /**
     * Determine whether the interceptor should kick in, that is,
     * whether the {@code invokeUnderTrace} method should be called.
     * <p>Default behavior is to check whether the given {@code Log}
     * instance is enabled. Subclasses can override this to apply the
     * interceptor in other cases as well.
     * @param invocation the {@code MethodInvocation} being traced
     * @param logger the {@code Log} instance to use
     * @return {@code true} if the {@code invokeUnderTrace} method
     * should be called; {@code false} otherwise
     */
    protected boolean isInterceptorEnabled(MethodInvocation invocation, Log logger);
    
    /**
     * Subclasses must override this method to perform any tracing around the
     * supplied {@code MethodInvocation}. Subclasses are responsible for
     * ensuring that the {@code MethodInvocation} actually executes by
     * calling {@code MethodInvocation.proceed()}.
     * <p>By default, the passed-in {@code Log} instance will have log level
     * "trace" enabled. Subclasses do not have to check for this again, unless
     * they overwrite the {@code isInterceptorEnabled} method to modify
     * the default behavior, and may delegate to {@code writeToLog} for actual
     * messages to be written.
     * @param invocation the method invocation to proceed with
     * @param logger the {@code Log} to write trace messages to
     * @return the result of the call to {@code MethodInvocation.proceed()}
     * @throws Throwable if the interceptor or the target object throws an exception
     * @see #writeToLog(Log, String)
     * @see #writeToLog(Log, String, Throwable)
     */
    protected abstract Object invokeUnderTrace(MethodInvocation invocation, Log logger) throws Throwable;
    
    /**
     * Write the supplied trace message to the supplied {@code Log} instance.
     * <p>To be called by {@code invokeUnderTrace} for enter/exit outcomes.
     * @param logger the {@code Log} instance to write to
     * @param message the message to write
     * @see #invokeUnderTrace
     */
    protected void writeToLog(Log logger, String message);
    
    /**
     * Write the supplied trace message and {@link Throwable} to the
     * supplied {@code Log} instance.
     * <p>To be called by {@code invokeUnderTrace} for exceptional outcomes.
     * @param logger the {@code Log} instance to write to
     * @param message the message to write
     * @param ex the exception that was thrown
     * @see #invokeUnderTrace
     */
    protected void writeToLog(Log logger, String message, Throwable ex);
}

Concurrency Control Interceptors

Interceptors for managing concurrent access to methods.

public class ConcurrencyThrottleInterceptor extends ConcurrencyThrottleSupport
        implements MethodInterceptor, Serializable {
    
    /**
     * Create a new ConcurrencyThrottleInterceptor.
     * Default concurrency limit is 1 (no concurrency).
     */
    public ConcurrencyThrottleInterceptor();
    
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable;
}

public abstract class ConcurrencyThrottleSupport implements Serializable {
    /** Transient to optimize serialization. */
    private transient Object monitor = new Object();
    
    private int concurrencyLimit = 1;
    
    private int concurrencyCount = 0;
    
    /**
     * Set the maximum number of concurrent access attempts that are allowed.
     * -1 indicates no concurrency limit at all.
     * <p>In principle, this limit can be changed at runtime,
     * although it is generally designed as a config time setting.
     * NOTE: Do not switch between -1 and any concrete limit at runtime,
     * as this will lead to inconsistent concurrency counts.
     */
    public void setConcurrencyLimit(int concurrencyLimit);
    
    /**
     * Return the maximum number of concurrent access attempts allowed.
     */
    public int getConcurrencyLimit();
    
    /**
     * Return whether this throttle is currently active.
     * @return {@code true} if the concurrency limit is active;
     * {@code false} if the concurrency limit is not active
     */
    public boolean isThrottleActive();
    
    /**
     * Return the current number of concurrent access attempts.
     */
    public int getConcurrencyCount();
    
    /**
     * To be invoked before the main execution logic of concrete subclasses.
     * <p>This implementation applies the concurrency throttle.
     * @throws IllegalStateException if the concurrency limit has been reached
     */
    protected void beforeAccess();
    
    /**
     * To be invoked after the main execution logic of concrete subclasses.
     * @see #beforeAccess()
     */
    protected void afterAccess();
}

Asynchronous Execution Interceptors

Interceptors for executing methods asynchronously.

public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport
        implements MethodInterceptor, Ordered {
    
    /**
     * Create a new instance with a default {@link AsyncUncaughtExceptionHandler}.
     * @param defaultExecutor the {@code Executor} (typically a Spring {@code AsyncTaskExecutor}
     * or {@link java.util.concurrent.ExecutorService}) to delegate to,
     * unless a more specific executor has been requested via a qualifier on the async method,
     * in which case the executor will be looked up at invocation time against the enclosing bean factory
     * @see AsyncExecutionAspectSupport#getExecutorQualifier
     * @see AsyncExecutionAspectSupport#setBeanFactory
     * @since 4.2.6
     */
    public AsyncExecutionInterceptor(Executor defaultExecutor);
    
    /**
     * Create a new {@code AsyncExecutionInterceptor}.
     * @param defaultExecutor the {@code Executor} (typically a Spring {@code AsyncTaskExecutor}
     * or {@link java.util.concurrent.ExecutorService}) to delegate to,
     * unless a more specific executor has been requested via a qualifier on the async method,
     * in which case the executor will be looked up at invocation time against the enclosing bean factory
     * @param exceptionHandler the {@link AsyncUncaughtExceptionHandler} to use
     */
    public AsyncExecutionInterceptor(Executor defaultExecutor, AsyncUncaughtExceptionHandler exceptionHandler);
    
    @Override
    public Object invoke(final MethodInvocation invocation) throws Throwable;
    
    /**
     * Return the order value of this object.
     */
    @Override
    public int getOrder();
    
    /**
     * Set the order value of this object.
     */
    public void setOrder(int order);
}

public interface AsyncUncaughtExceptionHandler {
    /**
     * Handle the given uncaught exception thrown from an asynchronous method.
     * @param ex the exception thrown from the asynchronous method
     * @param method the asynchronous method
     * @param params the parameters used to invoke the method
     */
    void handleUncaughtException(Throwable ex, Method method, Object... params);
}

public class SimpleAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {
    /**
     * A default {@link AsyncUncaughtExceptionHandler} that simply logs the exception.
     */
    @Override
    public void handleUncaughtException(Throwable ex, Method method, Object... params);
}

Adapter Classes for Advice Types

Adapters that convert Spring advice types to AOP Alliance method interceptors.

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {
    /**
     * Create a new MethodBeforeAdviceInterceptor for the given advice.
     * @param advice the MethodBeforeAdvice to wrap
     */
    public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice);
    
    @Override
    public Object invoke(MethodInvocation mi) throws Throwable;
}

public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {
    /**
     * Create a new AfterReturningAdviceInterceptor for the given advice.
     * @param advice the AfterReturningAdvice to wrap
     */
    public AfterReturningAdviceInterceptor(AfterReturningAdvice advice);
    
    @Override
    public Object invoke(MethodInvocation mi) throws Throwable;
}

public class ThrowsAdviceInterceptor implements MethodInterceptor, AfterAdvice {
    /**
     * Create a new ThrowsAdviceInterceptor for the given ThrowsAdvice.
     * @param throwsAdvice the ThrowsAdvice to wrap
     */
    public ThrowsAdviceInterceptor(Object throwsAdvice);
    
    /**
     * Return the throws advice.
     * @return the throws advice
     */
    public Object getThrowsAdvice();
    
    /**
     * Return the number of applicable handler methods.
     */
    public int getHandlerMethodCount();
    
    @Override
    public Object invoke(MethodInvocation mi) throws Throwable;
}

Introduction Interceptors

Interceptors for implementing introductions (mixins).

public class DelegatingIntroductionInterceptor extends IntroductionInfoSupport
        implements IntroductionInterceptor {
    
    /**
     * Construct a new DelegatingIntroductionInterceptor, providing
     * a delegate that implements the interfaces to be introduced.
     * @param delegate the delegate that implements the introduced interfaces
     */
    public DelegatingIntroductionInterceptor(Object delegate);
    
    /**
     * Construct a new DelegatingIntroductionInterceptor.
     * The delegate will be the subclass, which must implement
     * additional interfaces.
     */
    protected DelegatingIntroductionInterceptor();
    
    /**
     * Both invoke the delegate, and check that it implements the
     * interface indicated by the method name. If not, throw an exception.
     */
    @Override
    public Object invoke(MethodInvocation mi) throws Throwable;
    
    /**
     * Is this method on an introduced interface?
     * @param mi the method invocation
     * @return whether the invoked method is on an introduced interface
     */
    protected final boolean isMethodOnIntroducedInterface(MethodInvocation mi);
}

public class DelegatePerTargetObjectIntroductionInterceptor extends IntroductionInfoSupport
        implements IntroductionInterceptor {
    
    /**
     * Create a new DelegatePerTargetObjectIntroductionInterceptor,
     * providing a delegate class that implements additional interfaces.
     * @param delegateClass class that implements the introduced interfaces
     */
    public DelegatePerTargetObjectIntroductionInterceptor(Class<?> delegateClass, Class<?>... introducedInterfaces);
    
    @Override
    public Object invoke(MethodInvocation mi) throws Throwable;
    
    /**
     * Create a new delegate for this introduction interceptor.
     * The default implementation simply calls the no-arg constructor.
     * <p>Subclasses can override to return any object that implements the interfaces.
     * For example, the delegate can be returned from a factory, or
     * it can be a mock object.
     * @return the delegate instance
     */
    protected Object createDelegate();
}

Usage Examples

Performance Monitoring

// Simple performance monitoring
PerformanceMonitorInterceptor performanceInterceptor = 
    new PerformanceMonitorInterceptor();
performanceInterceptor.setUseDynamicLogger(true);

// Custom performance interceptor
public class CustomPerformanceInterceptor implements MethodInterceptor {
    private final Map<String, Long> executionTimes = new ConcurrentHashMap<>();
    
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        String methodKey = invocation.getMethod().getDeclaringClass().getSimpleName() + 
                          "." + invocation.getMethod().getName();
        
        long start = System.nanoTime();
        try {
            Object result = invocation.proceed();
            long executionTime = System.nanoTime() - start;
            executionTimes.put(methodKey, executionTime / 1_000_000); // Convert to milliseconds
            
            if (executionTime > 1_000_000_000) { // Log if > 1 second
                System.out.println("SLOW METHOD: " + methodKey + " took " + 
                                 (executionTime / 1_000_000) + "ms");
            }
            
            return result;
        } catch (Exception e) {
            System.out.println("EXCEPTION in " + methodKey + ": " + e.getMessage());
            throw e;
        }
    }
    
    public Map<String, Long> getExecutionTimes() {
        return new HashMap<>(executionTimes);
    }
}

Custom Trace Interceptor

// Customizable trace interceptor with placeholders
CustomizableTraceInterceptor traceInterceptor = new CustomizableTraceInterceptor();
traceInterceptor.setUseDynamicLogger(true);
traceInterceptor.setEnterMessage(
    "Entering method '$[methodName]' on class '$[targetClassShortName]' with arguments: $[arguments]"
);
traceInterceptor.setExitMessage(
    "Exiting method '$[methodName]' with return value: '$[returnValue]' (took $[invocationTime]ms)"
);
traceInterceptor.setExceptionMessage(
    "Exception in method '$[methodName]': $[exception]"
);

Concurrency Control

// Limit concurrent access to sensitive methods
ConcurrencyThrottleInterceptor throttleInterceptor = new ConcurrencyThrottleInterceptor();
throttleInterceptor.setConcurrencyLimit(3); // Allow max 3 concurrent executions

// Custom concurrency interceptor with queueing
public class QueueingConcurrencyInterceptor implements MethodInterceptor {
    private final Semaphore semaphore;
    private final long timeoutMs;
    
    public QueueingConcurrencyInterceptor(int maxConcurrent, long timeoutMs) {
        this.semaphore = new Semaphore(maxConcurrent);
        this.timeoutMs = timeoutMs;
    }
    
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        if (!semaphore.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
            throw new ConcurrencyThrottleException(
                "Could not acquire permit within " + timeoutMs + "ms for method: " + 
                invocation.getMethod().getName()
            );
        }
        
        try {
            return invocation.proceed();
        } finally {
            semaphore.release();
        }
    }
}

Asynchronous Execution

// Configure async execution
@Configuration
@EnableAsync
public class AsyncConfig {
    
    @Bean
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("async-");
        executor.initialize();
        return executor;
    }
    
    @Bean
    public AsyncUncaughtExceptionHandler asyncUncaughtExceptionHandler() {
        return new SimpleAsyncUncaughtExceptionHandler();
    }
    
    @Bean
    public AsyncExecutionInterceptor asyncExecutionInterceptor() {
        return new AsyncExecutionInterceptor(taskExecutor(), asyncUncaughtExceptionHandler());
    }
}

// Custom async exception handler
public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
    private static final Logger logger = LoggerFactory.getLogger(CustomAsyncExceptionHandler.class);
    
    @Override
    public void handleUncaughtException(Throwable ex, Method method, Object... params) {
        logger.error("Async method '{}' threw an exception with parameters: {}", 
                    method.getName(), Arrays.toString(params), ex);
        
        // Could also send notifications, write to database, etc.
        if (ex instanceof RuntimeException) {
            // Handle specific exception types
            handleRuntimeException((RuntimeException) ex, method, params);
        }
    }
    
    private void handleRuntimeException(RuntimeException ex, Method method, Object... params) {
        // Custom handling for runtime exceptions
        logger.warn("Runtime exception in async method {}: {}", method.getName(), ex.getMessage());
    }
}

Introduction/Mixin Support

// Interface to be introduced
public interface Timestamped {
    Date getLastModified();
    void touch();
}

// Implementation of the introduced interface
public class TimestampedImpl implements Timestamped {
    private Date lastModified = new Date();
    
    @Override
    public Date getLastModified() {
        return lastModified;
    }
    
    @Override
    public void touch() {
        lastModified = new Date();
    }
}

// Using introduction interceptor
DelegatingIntroductionInterceptor introductionInterceptor = 
    new DelegatingIntroductionInterceptor(new TimestampedImpl());

// Create proxy with introduction
ProxyFactory factory = new ProxyFactory(targetObject);
factory.addAdvice(introductionInterceptor);
factory.addInterface(Timestamped.class);

Object proxy = factory.getProxy();

// Now the proxy implements both original interfaces and Timestamped
if (proxy instanceof Timestamped) {
    ((Timestamped) proxy).touch();
    System.out.println("Last modified: " + ((Timestamped) proxy).getLastModified());
}

Complex Interceptor Chain

public class AuditInterceptor implements MethodInterceptor {
    private final AuditService auditService;
    
    public AuditInterceptor(AuditService auditService) {
        this.auditService = auditService;
    }
    
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        String user = getCurrentUser();
        String methodName = invocation.getMethod().getName();
        String className = invocation.getThis().getClass().getSimpleName();
        
        AuditEntry entry = new AuditEntry(user, className, methodName, System.currentTimeMillis());
        
        try {
            Object result = invocation.proceed();
            entry.setSuccess(true);
            entry.setResult(result != null ? result.toString() : "null");
            return result;
        } catch (Exception e) {
            entry.setSuccess(false);
            entry.setError(e.getMessage());
            throw e;
        } finally {
            entry.setEndTime(System.currentTimeMillis());
            auditService.saveAuditEntry(entry);
        }
    }
    
    private String getCurrentUser() {
        // Get current user from security context
        return "current-user";
    }
}

// Combine multiple interceptors
ProxyFactory factory = new ProxyFactory(targetObject);
factory.addAdvice(new SecurityInterceptor());        // Security checks first
factory.addAdvice(new AuditInterceptor(auditService)); // Then audit
factory.addAdvice(new PerformanceMonitorInterceptor()); // Then performance monitoring
factory.addAdvice(new CacheInterceptor());           // Finally caching

Object proxy = factory.getProxy();

Install with Tessl CLI

npx tessl i tessl/maven-org-springframework--spring-aop

docs

advice-interceptors.md

aspectj-integration.md

auto-proxy.md

core-abstractions.md

index.md

pointcuts.md

proxy-creation.md

target-sources.md

tile.json