CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-springframework--spring

Comprehensive application framework and inversion of control container for the Java platform providing dependency injection, AOP, data access, transaction management, and web framework capabilities

Overview
Eval results
Files

aop.mddocs/

Aspect-Oriented Programming (AOP)

Spring AOP provides aspect-oriented programming implementation allowing you to define method interceptors and pointcuts to cleanly decouple cross-cutting concerns like logging, security, transactions, and caching from your business logic.

Maven Dependencies

<!-- Spring AOP -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.3.39</version>
</dependency>

<!-- AspectJ for advanced AOP features -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.7</version>
</dependency>

<!-- Enable AspectJ support -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.3.39</version>
</dependency>

Core Imports

// Core AOP interfaces
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.ThrowsAdvice;

// Pointcut and Advisor interfaces
import org.springframework.aop.Pointcut;
import org.springframework.aop.Advisor;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;

// Proxy creation
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.framework.AopProxy;
import org.springframework.aop.aspectj.AspectJProxyFactory;

// AspectJ annotations
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

// Configuration
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.aop.config.AopConfigUtils;

Core AOP Concepts

Advice Types

// Tag interface for all advice types
public interface Advice {
}

// Method interceptor that sits around method invocations
@FunctionalInterface
public interface MethodInterceptor extends Interceptor {
    Object invoke(MethodInvocation invocation) throws Throwable;
}

// Advice invoked before method execution
public interface MethodBeforeAdvice extends BeforeAdvice {
    void before(Method method, Object[] args, Object target) throws Throwable;
}

// Advice invoked after successful method execution  
public interface AfterReturningAdvice extends AfterAdvice {
    void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;
}

// Advice invoked after method throws exception
public interface ThrowsAdvice extends AfterAdvice {
    // Marker interface - implementations should have methods like:
    // public void afterThrowing(Exception ex)
    // public void afterThrowing(Method method, Object[] args, Object target, Exception ex)
}

Pointcut and Advisor

// Core interface expressing where advice should be applied
public interface Pointcut {
    ClassFilter getClassFilter();
    MethodMatcher getMethodMatcher();
    
    Pointcut TRUE = TruePointcut.INSTANCE;
}

// Interface for matching classes
@FunctionalInterface
public interface ClassFilter {
    boolean matches(Class<?> clazz);
    ClassFilter TRUE = TrueClassFilter.INSTANCE;
}

// Interface for matching methods
public interface MethodMatcher {
    boolean matches(Method method, Class<?> targetClass);
    boolean isRuntime();
    boolean matches(Method method, Class<?> targetClass, Object... args);
    
    MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
}

// Base interface holding advice and filter determining applicability
public interface Advisor {
    Advice getAdvice();
    boolean isPerInstance();
}

// Superinterface for all Advisors driven by Pointcut
public interface PointcutAdvisor extends Advisor {
    Pointcut getPointcut();
}

Proxy Creation

// Delegate interface for AOP proxy creation
public interface AopProxy {
    Object getProxy();
    Object getProxy(ClassLoader classLoader);
}

// Factory for AOP proxies for programmatic use
public class ProxyFactory extends AdvisedSupport {
    public ProxyFactory();
    public ProxyFactory(Object target);
    public ProxyFactory(Class<?>... proxyInterfaces);
    public ProxyFactory(Class<?> proxyInterface, Interceptor interceptor);
    
    public Object getProxy();
    public Object getProxy(ClassLoader classLoader);
    
    // Static convenience methods
    public static <T> T getProxy(Class<T> proxyInterface, Interceptor interceptor);
    public static <T> T getProxy(Class<T> proxyInterface, TargetSource targetSource);
}

// AspectJ-based extension of ProxyFactory
public class AspectJProxyFactory extends ProxyCreatorSupport {
    public AspectJProxyFactory();
    public AspectJProxyFactory(Object target);
    
    public void addAspect(Object aspectInstance);
    public void addAspect(Class<?> aspectClass);
    public <T> T getProxy();
    public <T> T getProxy(ClassLoader classLoader);
}

AspectJ Integration

Enabling AspectJ Support

// Enables support for handling components marked with AspectJ's @Aspect annotation
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
    boolean proxyTargetClass() default false;
    boolean exposeProxy() default false;
}

// Configuration class enabling AspectJ auto proxy
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
    // AspectJ aspects will be automatically detected and applied
}

AspectJ Annotations

// Marks a class as an aspect
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Aspect {
    String value() default "";
}

// Declares a pointcut
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Pointcut {
    String value() default "";
    String argNames() default "";
}

// Before advice
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Before {
    String value();
    String argNames() default "";
}

// After returning advice
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)  
public @interface AfterReturning {
    String value() default "";
    String pointcut() default "";
    String returning() default "";
    String argNames() default "";
}

// After throwing advice
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AfterThrowing {
    String value() default "";
    String pointcut() default "";
    String throwing() default "";
    String argNames() default "";
}

// After (finally) advice
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface After {
    String value();
    String argNames() default "";
}

// Around advice
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Around {
    String value();
    String argNames() default "";
}

Join Points

// Runtime context for advice execution
public interface JoinPoint {
    String toString();
    String toShortString();
    String toLongString();
    Object getThis();
    Object getTarget();
    Object[] getArgs();
    Signature getSignature();
    SourceLocation getSourceLocation();
    String getKind();
    StaticPart getStaticPart();
}

// For around advice that can control method execution
public interface ProceedingJoinPoint extends JoinPoint {
    Object proceed() throws Throwable;
    Object proceed(Object[] args) throws Throwable;
}

// Signature information
public interface Signature {
    String toString();
    String toShortString();
    String toLongString();
    String getName();
    int getModifiers();
    Class<?> getDeclaringType();
    String getDeclaringTypeName();
}

public interface MethodSignature extends CodeSignature {
    Class<?> getReturnType();
    Method getMethod();
}

Practical Usage Examples

Basic AspectJ Aspects

// Logging aspect
@Aspect
@Component
public class LoggingAspect {
    
    private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
    
    // Pointcut for all service methods
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceLayer() {}
    
    // Pointcut for all repository methods  
    @Pointcut("execution(* com.example.repository.*.*(..))")
    public void repositoryLayer() {}
    
    // Combined pointcut
    @Pointcut("serviceLayer() || repositoryLayer()")
    public void businessLayer() {}
    
    // Before advice
    @Before("businessLayer()")
    public void logBefore(JoinPoint joinPoint) {
        logger.info("Calling method: {}.{}() with args: {}", 
            joinPoint.getTarget().getClass().getSimpleName(),
            joinPoint.getSignature().getName(),
            Arrays.toString(joinPoint.getArgs()));
    }
    
    // After returning advice
    @AfterReturning(pointcut = "businessLayer()", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        logger.info("Method {}.{}() returned: {}", 
            joinPoint.getTarget().getClass().getSimpleName(),
            joinPoint.getSignature().getName(),
            result);
    }
    
    // After throwing advice
    @AfterThrowing(pointcut = "businessLayer()", throwing = "exception")
    public void logAfterThrowing(JoinPoint joinPoint, Exception exception) {
        logger.error("Method {}.{}() threw exception: {}", 
            joinPoint.getTarget().getClass().getSimpleName(),
            joinPoint.getSignature().getName(),
            exception.getMessage(), exception);
    }
}

Performance Monitoring Aspect

@Aspect
@Component
public class PerformanceAspect {
    
    private static final Logger logger = LoggerFactory.getLogger(PerformanceAspect.class);
    
    @Around("@annotation(Timed)")
    public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        
        try {
            Object result = joinPoint.proceed();
            return result;
        } finally {
            long endTime = System.currentTimeMillis();
            long executionTime = endTime - startTime;
            
            logger.info("Method {}.{}() executed in {} ms",
                joinPoint.getTarget().getClass().getSimpleName(),
                joinPoint.getSignature().getName(),
                executionTime);
        }
    }
    
    @Around("execution(* com.example.service.*.*(..))")
    public Object monitorServiceMethods(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        StopWatch stopWatch = new StopWatch();
        
        stopWatch.start();
        try {
            Object result = joinPoint.proceed();
            return result;
        } finally {
            stopWatch.stop();
            logger.debug("Service method {} took {} ms", methodName, stopWatch.getTotalTimeMillis());
        }
    }
}

// Custom annotation for marking methods to be timed
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Timed {
    String value() default "";
}

Security Aspect

@Aspect
@Component
public class SecurityAspect {
    
    @Autowired
    private SecurityService securityService;
    
    @Before("@annotation(requiresRole)")
    public void checkRole(JoinPoint joinPoint, RequiresRole requiresRole) {
        String[] requiredRoles = requiresRole.value();
        
        if (!securityService.hasAnyRole(requiredRoles)) {
            throw new SecurityException("Access denied. Required roles: " + 
                Arrays.toString(requiredRoles));
        }
    }
    
    @Around("@annotation(requiresPermission)")
    public Object checkPermission(ProceedingJoinPoint joinPoint, RequiresPermission requiresPermission) throws Throwable {
        String permission = requiresPermission.value();
        
        if (!securityService.hasPermission(permission)) {
            throw new SecurityException("Access denied. Required permission: " + permission);
        }
        
        return joinPoint.proceed();
    }
}

// Security annotations
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresRole {
    String[] value();
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresPermission {
    String value();
}

// Usage in service classes
@Service
public class UserService {
    
    @RequiresRole({"ADMIN", "USER_MANAGER"})
    public User createUser(User user) {
        // Create user logic
        return user;
    }
    
    @RequiresPermission("user:delete")
    public void deleteUser(Long userId) {
        // Delete user logic
    }
}

Caching Aspect

@Aspect
@Component
public class CachingAspect {
    
    private final CacheManager cacheManager;
    
    public CachingAspect(CacheManager cacheManager) {
        this.cacheManager = cacheManager;
    }
    
    @Around("@annotation(cacheable)")
    public Object cache(ProceedingJoinPoint joinPoint, Cacheable cacheable) throws Throwable {
        String cacheName = cacheable.value();
        String key = generateKey(joinPoint);
        
        // Check cache first
        Object cached = cacheManager.get(cacheName, key);
        if (cached != null) {
            return cached;
        }
        
        // Execute method and cache result
        Object result = joinPoint.proceed();
        cacheManager.put(cacheName, key, result);
        
        return result;
    }
    
    @After("@annotation(cacheEvict)")
    public void evictCache(JoinPoint joinPoint, CacheEvict cacheEvict) {
        String cacheName = cacheEvict.value();
        if (cacheEvict.allEntries()) {
            cacheManager.clear(cacheName);
        } else {
            String key = generateKey(joinPoint);
            cacheManager.evict(cacheName, key);
        }
    }
    
    private String generateKey(JoinPoint joinPoint) {
        StringBuilder keyBuilder = new StringBuilder();
        keyBuilder.append(joinPoint.getSignature().getName());
        
        for (Object arg : joinPoint.getArgs()) {
            keyBuilder.append(":").append(arg != null ? arg.toString() : "null");
        }
        
        return keyBuilder.toString();
    }
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Cacheable {
    String value();
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheEvict {
    String value();
    boolean allEntries() default false;
}

Programmatic AOP with ProxyFactory

// Using ProxyFactory directly for programmatic AOP
public class AopExample {
    
    public void demonstrateProxyFactory() {
        // Create target object
        UserService target = new UserServiceImpl();
        
        // Create proxy factory
        ProxyFactory proxyFactory = new ProxyFactory(target);
        
        // Add advice
        proxyFactory.addAdvice(new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                System.out.println("Before method: " + invocation.getMethod().getName());
                
                try {
                    Object result = invocation.proceed();
                    System.out.println("After method: " + invocation.getMethod().getName());
                    return result;
                } catch (Exception e) {
                    System.out.println("Exception in method: " + invocation.getMethod().getName());
                    throw e;
                }
            }
        });
        
        // Get proxy
        UserService proxy = (UserService) proxyFactory.getProxy();
        
        // Use proxy - advice will be applied
        User user = proxy.findById(1L);
    }
}

// Method-specific interception
public class ValidationInterceptor implements MethodInterceptor {
    
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod();
        Object[] args = invocation.getArguments();
        
        // Validate arguments before method execution
        for (Object arg : args) {
            if (arg == null) {
                throw new IllegalArgumentException("Null argument not allowed for method: " + method.getName());
            }
        }
        
        // Proceed with method execution
        return invocation.proceed();
    }
}

// Using specific pointcuts
public class ServicePointcutAdvisor extends DefaultPointcutAdvisor {
    
    public ServicePointcutAdvisor() {
        // Create pointcut that matches all methods in service classes
        Pointcut pointcut = new StaticMethodMatcherPointcut() {
            @Override
            public boolean matches(Method method, Class<?> targetClass) {
                return targetClass.getSimpleName().endsWith("Service");
            }
        };
        
        setPointcut(pointcut);
        setAdvice(new LoggingInterceptor());
    }
}

Advanced Pointcut Expressions

@Aspect
@Component
public class AdvancedPointcutAspect {
    
    // Method execution pointcuts
    @Pointcut("execution(public * com.example..*(..))")
    public void publicMethods() {}
    
    @Pointcut("execution(* com.example.service.*.save*(..))")
    public void saveMethods() {}
    
    @Pointcut("execution(* com.example.repository.*Repository.find*(..))")
    public void findMethods() {}
    
    // Annotation-based pointcuts
    @Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
    public void transactionalMethods() {}
    
    @Pointcut("@within(org.springframework.stereotype.Service)")
    public void serviceClasses() {}
    
    // Type-based pointcuts
    @Pointcut("within(com.example.service..*)")
    public void withinServicePackage() {}
    
    @Pointcut("target(com.example.service.UserService)")
    public void userServiceTarget() {}
    
    // Argument-based pointcuts
    @Pointcut("args(java.lang.String, ..)")
    public void methodsWithStringFirstArg() {}
    
    @Pointcut("args(userId) && execution(* com.example.service.*.*(..))")
    public void serviceMethodsWithUserId(Long userId) {}
    
    // Bean pointcuts
    @Pointcut("bean(*Service)")
    public void serviceBeans() {}
    
    @Pointcut("bean(userService)")
    public void userServiceBean() {}
    
    // Complex combinations
    @Around("(serviceClasses() || repositoryClasses()) && publicMethods() && !execution(* toString())")
    public Object aroundBusinessMethods(ProceedingJoinPoint joinPoint) throws Throwable {
        // Complex advice logic
        return joinPoint.proceed();
    }
    
    // Pointcut with argument binding
    @Before("execution(* com.example.service.*.update*(..)) && args(entity)")
    public void beforeUpdate(Object entity) {
        System.out.println("Updating entity: " + entity);
    }
    
    // Multiple argument binding
    @Before("execution(* com.example.service.UserService.updateUser(..)) && args(userId, userData)")
    public void beforeUserUpdate(Long userId, UserData userData) {
        System.out.println("Updating user " + userId + " with data: " + userData);
    }
}

Integration with Spring Features

// AOP with Spring Boot auto-configuration
@SpringBootApplication
@EnableAspectJAutoProxy
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

// Conditional aspects based on properties
@Aspect
@Component
@ConditionalOnProperty(name = "app.audit.enabled", havingValue = "true")
public class AuditAspect {
    
    @AfterReturning(pointcut = "@annotation(Auditable)", returning = "result")
    public void auditMethod(JoinPoint joinPoint, Object result) {
        // Audit logic
    }
}

// Profile-specific aspects
@Aspect  
@Component
@Profile("development")
public class DebugAspect {
    
    @Around("execution(* com.example..*(..))")
    public Object debugMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("DEBUG: Entering " + joinPoint.getSignature().getName());
        Object result = joinPoint.proceed();
        System.out.println("DEBUG: Exiting " + joinPoint.getSignature().getName());
        return result;
    }
}

// Integration with other Spring features
@Aspect
@Component
public class IntegrationAspect {
    
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    
    @Autowired
    private MeterRegistry meterRegistry;
    
    @AfterReturning("execution(* com.example.service.OrderService.createOrder(..))")
    public void afterOrderCreated(JoinPoint joinPoint) {
        // Publish application event
        eventPublisher.publishEvent(new OrderCreatedEvent(this, "Order created"));
        
        // Update metrics
        meterRegistry.counter("orders.created").increment();
    }
}

Configuration and Best Practices

XML Configuration (Alternative)

<!-- Enable AspectJ auto proxy in XML -->
<aop:aspectj-autoproxy proxy-target-class="true"/>

<!-- Register aspect as bean -->
<bean id="loggingAspect" class="com.example.LoggingAspect"/>

<!-- Programmatic AOP configuration -->
<aop:config>
    <aop:aspect ref="loggingAspect">
        <aop:pointcut id="businessMethods" 
                      expression="execution(* com.example.service.*.*(..))"/>
        <aop:before method="logBefore" pointcut-ref="businessMethods"/>
        <aop:after-returning method="logAfterReturning" 
                            pointcut-ref="businessMethods" 
                            returning="result"/>
    </aop:aspect>
</aop:config>

Performance Considerations

// Optimize pointcut expressions for better performance
@Aspect
@Component
public class OptimizedAspect {
    
    // More specific pointcuts perform better
    @Pointcut("execution(* com.example.service.UserService.*(..))") // Good
    public void userServiceMethods() {}
    
    @Pointcut("execution(* *.*(..))") // Avoid - too broad
    public void allMethods() {}
    
    // Use within() for better performance when targeting packages
    @Pointcut("within(com.example.service..*)")
    public void servicePackage() {}
    
    // Combine pointcuts efficiently  
    @Pointcut("servicePackage() && execution(public * save*(..))")
    public void publicSaveMethods() {}
}

Spring AOP provides a powerful and flexible way to implement cross-cutting concerns in your applications. It integrates seamlessly with the Spring IoC container and provides both annotation-driven and programmatic approaches to aspect-oriented programming.

Install with Tessl CLI

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

docs

aop.md

core-container.md

data-access.md

index.md

integration.md

messaging.md

reactive-web.md

testing.md

web-framework.md

tile.json