Core Guides

Aspectran AOP: Features and Architecture

Aspect-Oriented Programming (AOP) is a foundational technique that reduces code complexity by modularizing cross-cutting concerns—such as logging, security, transactions, response header injection, and global exception handling—away from core business logic.

While Spring AOP primarily focuses on proxying bean method invocations, Aspectran provides a highly optimized, pragmatic AOP architecture built upon two distinct pillars: the Activity Lifecycle and Selective Bean Proxying.

1. The Two Architectural Pillars of Aspectran AOP

Aspectran AOP offers two dimensions of join points and interception mechanisms tailored to their respective architectural purposes.

Architectural PillarTargetMechanismPrimary Use Cases
Activity Lifecycle AOP
(Translet Lifecycle)
Activity (The entire request-processing flow)Zero proxy overhead; the CoreActivity engine directly executes aspects according to lifecycle stages (Non-Proxy Core Interception)Web security response headers, global character encoding and view dispatcher injection, pre/post-processing, authentication/authorization, global exception handling
Bean Proxy AOP
(Method Interception)
Bean method invocations (JoinpointTargetType.METHOD)Runtime dynamic proxy (AbstractBeanProxy, Javassist/JDK) intercepts methods annotated with @AdvisableDeclarative database transactions, business method profiling, audit logging

1.1. Activity Lifecycle AOP (Non-Proxy Core Interception)

In Aspectran, incoming client requests (Web, Daemon, Shell) are executed by an Activity instance. The Activity engine traverses predefined lifecycle stages from initiation to completion, directly invoking matched aspects without intermediate proxy overhead.

StageLifecycle StagePrimary Tasks & Responsibilities
Stage 1Before AdviceExecutes pre-processing logic, injects <settings> (encoding, view dispatcher) and security <headers>, validates authentication
Stage 2Translet Action ExecutionExecutes core business actions and process routines, generating response data
Stage 3After AdvicePerforms post-processing on results, records audit logs
Stage 4Exception Handling (<exception>)Catches exceptions and maps them to error view pages (<dispatch>) or RESTful JSON (<transform>) error responses
Stage 5Finally AdviceAlways runs at the end regardless of success or failure to clean up resources and session states
  • Zero Overhead: Directly executed within the framework core flow without proxy creation or reflective overhead, ensuring maximum throughput.
  • Structural Flow Control: Beyond simple method interception, it declaratively injects and manages request context (Activity) attributes such as encoding, view dispatchers, security headers, and global exception screens.

1.2. Bean Proxy AOP (Selective Dynamic Proxying)

When applying AOP at the level of individual service methods, Aspectran employs dynamic proxies based on bytecode generation with Javassist (JavassistProxyBean) or standard JDK Dynamic Proxies (JdkDynamicProxyBean).

  • @Advisable Selective Proxying (Performance Optimization):
    • Dynamic proxies do not indiscriminately intercept every method invocation.
    • Pointcut evaluation and advice chains are triggered only for methods annotated with @Advisable.
    • Unannotated regular methods bypass the proxy chain entirely, immediately delegating to the target method with zero proxy overhead.

2. Core Components of the <aspect> Rule

In Aspectran, aspects can be configured declaratively via <aspect> XML rules or Java annotations.

2.1. Fundamental Aspect Attributes

<aspect id="sampleAspect" order="0" isolated="true" disabled="false">
    ...
</aspect>
AttributeDescription
idUnique identifier of the Aspect.
orderDefines the execution precedence among multiple aspects. Lower integer values denote higher priority (default: Integer.MAX_VALUE). If two aspects have identical order values, the one declared first takes precedence.
isolatedConfigures Exception Isolation Mode (true/false). When isolated="true", any unhandled exception occurring inside the aspect’s advice will not halt the main request flow; an error log is recorded and the request proceeds normally. Ideal for non-critical cross-cutting concerns like statistics, monitoring, or external telemetry.
disabledWhen set to true, disables the aspect at runtime.

2.2. Precision Joinpoint and Pointcut Control (<joinpoint>)

The <joinpoint> element precisely specifies where and when advice should be triggered.

<joinpoint target="activity">
    methods: [
        GET
        POST
    ]
    headers: [
        "Accept=text/html"
        "Origin"
    ]
    pointcut: {
        type: wildcard
        +: /user/**@userService^get*
        +: /order/**
        -: /order/temp-*
    }
</joinpoint>
Attribute / ElementDefaultDescription
targetactivitySpecifies the join point target type.
• activity: Intercepts the entire request lifecycle stages of Translets (Non-Proxy Core Interception).
• method: Intercepts bean methods annotated with @Advisable via dynamic proxies (Selective Dynamic Proxying).
methodsAllRestricts aspect execution to specific HTTP request methods (GET, POST, etc.).
headersAllEvaluates specific client request headers to determine aspect execution.
pointcutAllGranular filtering combining Translet names, Bean IDs, Class names, and Method patterns.

a. Pointcut Expression Structure

Pointcuts are defined in APON format and follow this pattern:

\[\text{transletPattern}[\text{@beanOrClassPattern}][\text{^methodNamePattern}]\]

A pointcut expression consists of three distinct segments delimited by @ and ^, allowing precise targeting combinations. Each segment is optional and can be omitted as needed.

  • Delimiters:
    • @ (Bean/Class Delimiter): Separates the Translet pattern from the Bean/Class pattern.
    • ^ (Method Delimiter): Separates the Bean/Class pattern from the Method pattern.
  • Segment Components:
    • Translet Pattern (Pre-@): Matches the target Translet name or request URI path pattern (e.g., /user/**, /api/v1/*). If omitted, all Translets are targeted.
    • Bean ID Pattern (Post-@, Pre-^): Matches the target Bean ID pattern (e.g., @userService, @*Service). Specifying an identifier without any prefix directive is interpreted as a Bean ID.
    • Class Pattern (Post-@class:, Pre-^): Matches fully-qualified package/class name patterns. To target class types, the class: directive prefix is required (e.g., @class:com.mycompany.service.*, @class:*.UserServiceImpl). This is essential for targeting anonymous beans (beans without an ID) or all implementations of a specific package/interface.
    • Method Pattern (Post-^): Matches the target method name pattern (e.g., ^get*, ^save*, ^execute). If omitted, all methods of the target bean or the entire Activity lifecycle are targeted.
Pattern FormatPattern ExampleTarget Description
transletPattern/order/**Targets the entire Activity lifecycle of all Translets under /order/
transletPattern@beanId/user/*@userDaoTargets the userDao bean executing within /user/* Translets
transletPattern@class:className/api/**@class:com.mycompany.dao.*Targets beans of class types in that package within /api/** Translets
transletPattern@beanId^methodName/user/*@userService^get*Targets methods starting with get on the userService bean within /user/* Translets
transletPattern@class:className^methodName/translet@class:hello.Simplest^hello*Targets methods starting with hello on the hello.Simplest class within /translet requests
@beanId@orderServiceTargets the orderService bean across all Translets
@class:className@class:com.mycompany.service.*Targets beans under the com.mycompany.service package across all Translets
@beanId^methodName@orderService^process*Targets methods starting with process on the orderService bean across all Translets
@class:className^methodName@class:com.mycompany.service.*Service^process*Targets methods starting with process on all Service classes across all Translets
@^methodName@^execute*Targets methods starting with execute on all beans regardless of Translet or bean type
  • Advanced Pattern Matching Features:
    • Single-Segment Wildcard (*): Matches zero or more characters within a single segment excluding delimiters (/ or .) (e.g., /api/* matches /api/users but not /api/v1/users; com.example.*Service matches com.example.UserService).
    • Multi-Segment Wildcard (**): Matches across multiple path or package hierarchy levels including delimiters (e.g., /user/** matches /user/list and /user/a/b/c; @class:com.mycompany.**Service matches com.mycompany.order.OrderService).
    • Single-Character Wildcard (?): Matches exactly one arbitrary character.
    • Multi-Pattern OR Matching (|): Connects multiple patterns using the pipeline (|) symbol to evaluate OR conditions within a single rule (e.g., +: /user/**|/order/**@userService|orderService^get*|find*).
    • Hierarchical Path and Namespace Matching: Translet names use the slash (/) delimiter, while Bean IDs and class names use the dot (.) delimiter for structured matching.

b. Include (+:) and Exclude (-:) Rules with Top-Down Evaluation

Aspectran evaluates pointcut rules sequentially in top-down declaration order:

  • +: (Include): Includes matched targets into the AOP execution scope.
  • -: (Exclude): Excludes matched targets from the AOP execution scope.

💡 Note (Order-based Granular Control): You can easily define complex filtering by including a broad range at the top (+:) and excluding specific exceptions underneath (-:).

Wildcard Pointcut Example (type: wildcard):

<joinpoint>
    pointcut: {
        type: wildcard
        +: /api/**                     # 1. Include all endpoints under /api/
        -: /api/auth/login             # 2. Exclude login endpoint from auth check
        -: /api/health-check           # 3. Exclude health check endpoint
        +: /admin/**@adminService^*    # 4. Include adminService methods under /admin/
        -: /admin/**@adminService^get* # 5. Exclude read-only methods starting with 'get'
    }
</joinpoint>

Regular Expression Pointcut Example (type: regexp): When using regular expressions, separate include and exclude blocks allow strict regex matching.

<joinpoint>
    pointcut: {
        type: regexp
        include: {
            translet: "^/api/v[1-9]/.*"
            bean: "^(user|order)Service$"
            method: "^(create|update|delete).*"
        }
        exclude: {
            translet: "^/api/v.*/temp-.*"
            bean: "^.*TestBean$"
        }
    }
</joinpoint>

c. Request Method (methods) Filtering

Restricts aspect execution to specific HTTP request methods such as GET, POST, PUT, DELETE, or PATCH.

d. Request Header (headers) Filtering

Controls aspect execution based on client request headers:

  • Header Presence: "Origin" (Matches if the header exists)
  • Exact Value Matching: "X-Requested-With=XMLHttpRequest" (Matches if the header equals the value)
  • Negative Comparison: "Accept!=application/json" (Matches if the header does not contain the value)
  • Complex Media Type Matching (Web Environment): Analyzes complex browser Accept headers (e.g., text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8) with quality factors (q-values) to accurately determine if it is an HTML page request ("Accept=text/html").

2.3. Settings Context Injection (<settings>)

The <settings> element injects configuration parameters directly into the matched Translet’s Activity context, avoiding redundant per-Translet declarations.

<aspect id="webTransletSettings">
    <joinpoint>
        pointcut: {
            +: /**
        }
    </joinpoint>
    <settings>
        <setting name="characterEncoding" value="utf-8"/>
        <setting name="viewDispatcher" value="thymeleafViewDispatcher"/>
    </settings>
</aspect>
  • characterEncoding: Sets the default request/response character encoding.
  • viewDispatcher: Assigns the default ViewDispatcher bean for the matching scope.

2.4. Advice Types and Composition (<advice>)

Advice defines the concrete logic executed at a join point. Aspectran supports 5 standard advice types:

Advice TypeExecution TimingPrimary Use Cases
beforePrior to join point executionInput validation, security verification, response header injection
afterAfter successful join point executionResult data enrichment, audit logging
aroundSurrounds join point executionExecution profiling, transaction boundaries (begin & commit)
thrownUpon exceptionError logging, transaction rollback, alerting
finallyAlways executed at the endResource cleanup, session cleanup, ThreadLocal clearing

Inside <advice>, you can configure bean method invocations (<invoke>), action routines (<action>), and declarative response header injection (<headers>):

<advice bean="securityAdvice">
    <before>
        <!-- Declarative response header injection -->
        <headers>
            <item name="X-Frame-Options">SAMEORIGIN</item>
            <item name="X-Content-Type-Options">nosniff</item>
            <item name="X-XSS-Protection">1; mode=block</item>
        </headers>
        <!-- Advice bean method execution -->
        <invoke method="checkAuthentication"/>
    </before>
</advice>

2.5. Global Exception Handling (<exception>)

Defining an <exception> element within an aspect allows catching specific exceptions globally and mapping them to error view pages or structured RESTful JSON responses.

<aspect id="globalExceptionAspect">
    <joinpoint>
        pointcut: {
            +: /**
        }
    </joinpoint>
    <exception>
        <!-- Maps specific exception to a dedicated error page -->
        <thrown type="com.mycompany.common.exception.UserNotFoundException">
            <dispatch name="error/user-not-found" contentType="text/html" encoding="UTF-8"/>
        </thrown>
        <!-- Generic fallback for unhandled exceptions -->
        <thrown type="java.lang.Throwable">
            <action bean="errorLogger" method="logError"/>
            <dispatch name="error/500" contentType="text/html" encoding="UTF-8"/>
        </thrown>
    </exception>
</aspect>

Differences Between <advice><thrown> and <exception><thrown>

When defining behavior for raised exceptions, Aspectran provides two distinct mechanisms: <thrown> inside <advice> and <thrown> inside <exception>. They differ fundamentally in purpose and exception propagation:

Category<advice><thrown> (Exception Handling Advice)<exception><thrown> (Global Exception Response Mapping)
Primary PurposeExecutes supplementary logic upon exceptions (e.g., error logging, transaction rollback, alerting)Catches raised exceptions and maps them to dedicated error view pages or JSON error responses
Exception PropagationExecutes advice logic and continues to propagate (rethrow) the exception upwardCompletely catches and handles the exception, returning a normal error response to the client
Available ActionsBean method invocations (<invoke>), Action executions (<action>), Header injection (<headers>)View dispatching (<dispatch>), Data transformations (<transform>), Action executions (<action>)
Annotation EquivalentAdvice methods annotated with @ExceptionThrownTranslet/Aspect exception handling rule mappings

3. Annotation-Based AOP Configuration

Aspectran allows full AOP configuration using pure Java classes and annotations without XML.

3.1. Core AOP Annotations

AnnotationDescriptionKey Attributes
@AspectDeclares a class as an Aspect (used alongside @Component).id, order, isolated, disabled
@JoinpointConfigures pointcut matching criteria.pointcut, target, methods, headers
@BeforeDesignates a Before advice method.-
@AfterDesignates an After advice method.-
@AroundDesignates an Around advice method.-
@FinallyDesignates a Finally advice method.-
@ExceptionThrownDesignates an Exception advice method.value (Target exception class)
@SettingsInjects settings into the Activity context.name, value
@AdvisableMarks a bean method as a target for Bean Proxy AOP.-

3.2. Annotation-Based Aspect Implementation Example

package com.aspectran.demo.aspect;

import com.aspectran.core.activity.Activity;
import com.aspectran.core.activity.Translet;
import com.aspectran.core.component.bean.annotation.After;
import com.aspectran.core.component.bean.annotation.Aspect;
import com.aspectran.core.component.bean.annotation.Before;
import com.aspectran.core.component.bean.annotation.Component;
import com.aspectran.core.component.bean.annotation.ExceptionThrown;
import com.aspectran.core.component.bean.annotation.Finally;
import com.aspectran.core.component.bean.annotation.Joinpoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component
@Aspect(id = "loggingAspect", order = 1, isolated = true)
@Joinpoint(
    pointcut = {
        "+: /api/**",
        "-: /api/health-check"
    }
)
public class LoggingAspect {

    private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);

    @Before
    public void beforeRequest(Translet translet) {
        logger.info("[Request Start] {} {}", translet.getRequestMethod(), translet.getRequestName());
    }

    @After
    public void afterRequest(Translet translet) {
        logger.info("[Request Success] {}", translet.getRequestName());
    }

    @ExceptionThrown(Exception.class)
    public void onError(Translet translet) {
        Throwable e = translet.getRaisedException();
        logger.error("[Request Error] {} - {}", translet.getRequestName(), e.getMessage(), e);
    }

    @Finally
    public void onFinally(Translet translet) {
        // Perform resource cleanup
    }
}

4. Production Best Practice Patterns

Pattern 1: Global Web Security and Environment Auto-Injection

Automatically injects security response headers (CSP, X-Frame-Options) and default ViewDispatcher settings for all browser HTML requests.

<aspectran>
    <!-- 1. Register View Dispatcher Bean -->
    <bean id="thymeleafViewDispatcher" class="com.aspectran.thymeleaf.view.ThymeleafViewDispatcher">
        <argument>#{thymeleafEngine}</argument>
        <property name="contentType">text/html</property>
    </bean>

    <!-- 2. Global Web Translet Settings Aspect -->
    <aspect id="webTransletSettings">
        <joinpoint>
            pointcut: {
                +: /**
            }
        </joinpoint>
        <settings>
            <setting name="characterEncoding" value="utf-8"/>
            <setting name="viewDispatcher" value="thymeleafViewDispatcher"/>
        </settings>
        <advice>
            <before>
                <headers>
                    <item name="X-Frame-Options">SAMEORIGIN</item>
                    <item name="X-Content-Type-Options">nosniff</item>
                    <item name="X-XSS-Protection">1; mode=block</item>
                    <item name="Referrer-Policy">strict-origin-when-cross-origin</item>
                </headers>
            </before>
        </advice>
    </aspect>

    <!-- 3. HTML Browser Request Security Aspect -->
    <aspect id="htmlWebSecuritySettings">
        <joinpoint>
            headers: [
                "Accept=text/html"
            ]
            pointcut: {
                +: /**
            }
        </joinpoint>
        <advice>
            <before>
                <headers>
                    <item name="Content-Security-Policy">default-src 'self'; script-src 'self' 'unsafe-inline' cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' fonts.googleapis.com; font-src 'self' fonts.gstatic.com;</item>
                </headers>
            </before>
        </advice>
    </aspect>
</aspectran>

Pattern 2: Declarative Transaction Management (SqlSessionAdvice + @Advisable)

Completely decouples MyBatis SqlSession lifecycle management (open, commit, rollback, close) from business logic.

1. Transaction Aspect Configuration (mybatis-context.xml):

<!-- 1. SqlSessionAdvice Bean Definition -->
<bean id="sqlSessionTxAdvice" class="com.aspectran.mybatis.SqlSessionAdvice" scope="prototype">
    <argument>#{sqlSessionFactory}</argument>
</bean>

<!-- 2. Transaction Aspect Definition -->
<aspect id="txAspect" order="0">
    <joinpoint>
        pointcut: {
            +: **@simpleSqlSession
        }
    </joinpoint>
    <advice bean="sqlSessionTxAdvice">
        <before>
            <invoke method="open"/>
        </before>
        <after>
            <invoke method="commit"/>
        </after>
        <finally>
            <invoke method="close"/>
        </finally>
    </advice>
</aspect>

2. Transaction Usage in the Service Layer:

@Component
public class OrderService {

    private final SimpleSqlSession sqlSession;

    @Autowired
    public OrderService(SimpleSqlSession sqlSession) {
        this.sqlSession = sqlSession;
    }

    public void processOrder(Order order) {
        // Calling sqlSession methods automatically triggers txAspect to handle the transaction
        sqlSession.insert("app.demo.mapper.OrderMapper.insertOrder", order);
        sqlSession.update("app.demo.mapper.ItemMapper.updateStock", order.getItemId());
    }
}

Pattern 3: RESTful API Performance Profiling (Around Advice)

Monitors slow API requests using an Around advice with exception isolation.

package com.aspectran.demo.aspect;

import com.aspectran.core.activity.Translet;
import com.aspectran.core.component.bean.annotation.Around;
import com.aspectran.core.component.bean.annotation.Aspect;
import com.aspectran.core.component.bean.annotation.Component;
import com.aspectran.core.component.bean.annotation.Joinpoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component
@Aspect(id = "profilingAspect", isolated = true)
@Joinpoint(pointcut = "+: /api/**")
public class ProfilingAspect {

    private static final Logger logger = LoggerFactory.getLogger(ProfilingAspect.class);

    @Around
    public Object profile(Translet translet) throws Throwable {
        long startTime = System.currentTimeMillis();
        try {
            // Proceed with the request flow
            return null;
        } finally {
            long elapsedTime = System.currentTimeMillis() - startTime;
            if (elapsedTime > 500) {
                logger.warn("[SLOW QUERY/API] {} took {} ms", translet.getRequestName(), elapsedTime);
            }
        }
    }
}

5. Conclusion

Aspectran AOP replaces bulky proxy configurations with two finely-tuned mechanisms: Framework Core Lifecycle Interception (Activity) and Selective Bean Proxying (@Advisable).

  1. Activity Lifecycle AOP: Provides zero-overhead declarative control over global environment settings, security headers, authentication, and exception routing.
  2. Bean Proxy AOP: Selectively applies dynamic proxies only where necessary, efficiently managing transactions and business method profiling.

This dual-pillar architecture empowers developers to build clean, maintainable, and high-performance enterprise applications with minimal configuration overhead.