Aspect-Oriented Programming (AOP) is a key technique for reducing complexity by separating Cross-cutting Concerns—such as logging, security, and transactions—from core business logic.
Spring AOP offers powerful and granular control by combining bean method interception with AspectJ’s extensive expression language. However, this flexibility can sometimes lead to complex configurations and a steeper learning curve.
Aspectran takes a more pragmatic approach, focusing on structural efficiency rather than exhaustive control. It centers its AOP model around the lifecycle of the Activity, the core unit of request processing. This allows developers to intuitively manage the flow from request to response, while still providing essential bean method-level interception. Aspectran aims to simplify AOP by optimizing it for the framework’s execution flow, offering a lightweight yet effective solution for most enterprise needs.
Key Features
1. Join Point: Execution Timing
Aspectran AOP provides two main categories of Join Points:
- Activity Execution (Translet Lifecycle): This is Aspectran’s most distinct and powerful Join Point. It allows intervention throughout the lifecycle of an
Activity(which processes requests based on Translet rules)—such as before/after execution or on exception. This is ideal for modularizing cross-cutting concerns like logging, transactions, and authentication at the request level. - Bean Method Execution: Similar to other AOP frameworks, specific Bean method executions can serve as Join Points.
2. Pointcut: Target Selection
A Pointcut is an expression that precisely defines where Advice should be applied. In Aspectran, Pointcuts are declaratively defined via the <joinpoint> element within an <aspect> rule.
APON Format Definition: The
<joinpoint>element uses APON (Aspectran Parameter Object Notation) for detailed rule configuration, supporting wildcards (*) and regular expressions.- Expression Structure:
transletNamePattern[@beanOrClassPattern][^methodNamePattern]- Translet Pattern (Pre-
@): Specifies the target Translet name pattern. - Bean/Class Pattern (Post-
@): Specifies the target Bean ID or class name pattern. - Method Pattern (Post-
^): Specifies the target method name pattern.
- Translet Pattern (Pre-
- Examples:
- Specific Bean in ALL Translets: Omit Translet pattern, start with
@.<joinpoint> pointcut: { type: wildcard +: @someService^execute* } </joinpoint> - Specific Bean in Specific Translet:
<joinpoint> pointcut: { type: wildcard +: /user/list@userService^get* } </joinpoint> - Translet Itself: Omit Bean/Method patterns to target the Activity execution.
<joinpoint> pointcut: { type: wildcard +: /user/* } </joinpoint>
- Specific Bean in ALL Translets: Omit Translet pattern, start with
- Inclusion & Exclusion Rules:
- Use the
+prefix to include targets and the-prefix to exclude them. - Rules are applied in order, allowing for precise control (e.g., include all beans in a package, then exclude specific ones).
- Use the
3. Advice: Action Logic
- Types: Supports 5 standard advice types (
com.aspectran.core.context.rule.type.AdviceType):BEFORE: Prior to Join Point execution.AFTER: After successful execution.AROUND: Wraps execution (defines bothBEFOREandAFTER).THROWN: On exception.FINALLY: Always executed (regardless of success/failure).
- Execution Mechanism: Advice logic is implemented as a method within a Bean. The invoker depends on the target:
- Bean Method Advice: Handled by
com.aspectran.core.component.bean.proxy.AbstractBeanProxy. It intercepts the call, runs Advice, then invokes the original method. - Activity Advice: Handled by
com.aspectran.core.activity.CoreActivity. It invokes Advice at specific lifecycle moments (start, end, etc.).
- Bean Method Advice: Handled by
4. Aspect: Modularization
<aspect>Rule: Defines the combination of Advice (Bean method) and Pointcut. This encapsulates cross-cutting concerns into a single reusable module.
5. Weaving: Selective Dynamic Proxying
Aspectran uses a runtime Dynamic Proxy mechanism based on AbstractBeanProxy, optimized for performance.
- Zero Overhead for Non-Targeted Methods:
- The proxy performs AOP logic only if the method is annotated with
@Advisable. - Methods without this annotation bypass all AOP processing (including Pointcut matching) and immediately invoke the original method, eliminating unnecessary proxy overhead.
- The proxy performs AOP logic only if the method is annotated with
- Proxy Generation:
- Javassist (Default): Flexible proxy creation for classes and interfaces (
JavassistProxyBean). - JDK Dynamic Proxy: Available for Beans implementing interfaces (
JdkDynamicProxyBean).
- Javassist (Default): Flexible proxy creation for classes and interfaces (
6. Annotation Support
The com.aspectran.core.component.bean.annotation package allows full AOP configuration without XML.
@Component: Registers the class as a Bean via component scanning. An Aspect class must also have@Component(along with@Aspect) to be detected.- If used alone on an Aspect, it registers as an implicit Advice Bean without a specific ID.
@Bean: Used with@Componentto assign an explicit ID to the Advice Bean (e.g.,@Bean("myAdvice")).@Aspect: Marks a Bean as an Aspect. Attributes:id,order.@Joinpoint: Defines the Pointcut.@Before,@After,@Around,@Finally,@ExceptionThrown: Maps methods to Advice types.@Advisable: Explicitly marks a method for AOP interception.@Settings: Injects values into the currentActivitycontext.
7. Practical Example: Declarative Transactions
AOP is essential for separating transaction logic (begin, commit, rollback) from business logic. Below is a comparison of Annotation vs. XML configuration for MyBatis transactions.
1. Annotation-based Configuration
Define an Aspect extending SqlSessionAdvice.
SimpleTxAspect.java:
import com.aspectran.core.component.bean.annotation.*;
import com.aspectran.core.context.rule.type.ScopeType;
import com.aspectran.mybatis.SqlSessionAdvice;
import org.apache.ibatis.session.SqlSessionFactory;
@Component
@Bean(id = "simpleTxAspect", lazyDestroy = true)
@Scope(ScopeType.PROTOTYPE)
@Aspect(order = 0)
@Joinpoint(pointcut = "+: **@simpleSqlSession")
public class SimpleTxAspect extends SqlSessionAdvice {
@Autowired
public SimpleTxAspect(SqlSessionFactory sqlSessionFactory) {
super(sqlSessionFactory);
}
@Before
public void open() { super.open(); }
@After
public void commit() { super.commit(); }
@Finally
public void close() { super.close(); }
}
- Target: Applies to all public methods of the bean with ID
simpleSqlSession.
2. XML-based Configuration
Separates the Advice Bean and Aspect definition.
mybatis-context.xml:
<!-- 1. Advice Bean -->
<bean id="sqlSessionTxAdvice" class="com.aspectran.mybatis.SqlSessionAdvice" scope="prototype">
<arguments>
<item>#{sqlSessionFactory}</item>
</arguments>
</bean>
<!-- 2. Aspect Definition -->
<aspect id="simpleTxAspect" 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>
3. Service Layer Usage
1. Define the Transactional Resource The SimpleSqlSession bean connects to the Aspect via its constructor or ID matching.
@Component
@Bean(id = "simpleSqlSession")
public class SimpleSqlSession extends SqlSessionAgent {
public SimpleSqlSession() {
super("simpleTxAspect"); // Links to the Aspect ID
}
}
2. Apply in Business Logic Inject SimpleSqlSession into your service.
@Component
public class OrderService {
private final SimpleSqlSession sqlSession;
@Autowired
public OrderService(SimpleSqlSession sqlSession) {
this.sqlSession = sqlSession;
}
public void createOrder(Order order) {
// Direct database operation.
// The 'simpleTxAspect' automatically handles the transaction lifecycle around this call.
sqlSession.insert("app.demo.mapper.OrderMapper.insertOrder", order);
}
}
- How it works:
- When
OrderServicecallssqlSession.insert(...), it invokes the method on the injectedSimpleSqlSessionbean. - This triggers the
simpleTxAspectbecause the bean ID (simpleSqlSession) matches the Pointcut (**: @simpleSqlSession). - The
@Beforeadvice opens the transaction. - The database operation executes.
- Finally,
@Aftercommits the transaction, and@Finallycloses the session.
- When
By leveraging AOP, business logic is perfectly separated from transaction management code, greatly improving maintainability.
Summary
- Lifecycle Integration: Uniquely utilizes Translet/Activity lifecycles as Join Points.
- High Performance: Selective proxying via
@Advisableminimizes overhead. - Flexibility: Supports both XML and Annotation styles for diverse architectural needs.