0
# Aspect Management
1
2
Runtime support for aspect instantiation, lifecycle management, and access across different instantiation models. AspectJ supports various aspect instantiation patterns including singleton, per-object (perthis/pertarget), per-type-within, and control flow-based (percflow/percflowbelow) aspects. The aspect management API provides programmatic access to aspect instances and their binding status.
3
4
## Capabilities
5
6
### Aspects Class
7
8
The `Aspects` class handles generic `aspectOf` method functionality when those methods are not available in the aspects but added later through load time weaving. It provides reflective access to aspect instances across all supported instantiation models.
9
10
```java { .api }
11
/**
12
* Handles generic aspectOf method when those are not available in the aspects but added later on
13
* through load time weaving. Aspects.aspectOf(..) is doing reflective calls to the aspect aspectOf,
14
* so for better performance consider using ajc compilation of the aspects and using them as a binary
15
* dependencies in your project.
16
*/
17
public class Aspects {
18
/**
19
* Returns the singleton aspect or the percflow / percflowbelow associated with the current thread
20
* @param aspectClass the aspect class
21
* @return the singleton aspect or the percflow / percflowbelow associated with the current thread
22
* @throws NoAspectBoundException if no such aspect
23
*/
24
public static <T> T aspectOf(Class<T> aspectClass) throws NoAspectBoundException;
25
26
/**
27
* Returns the associated perthis / pertarget aspect instance
28
* @param aspectClass the aspect class
29
* @param perObject the this/target object for which to look for an aspect instance
30
* @return the associated perthis / pertarget aspect instance
31
* @throws NoAspectBoundException if no such aspect, or no aspect bound
32
*/
33
public static <T> T aspectOf(Class<T> aspectClass, Object perObject) throws NoAspectBoundException;
34
35
/**
36
* Returns the associated pertypewithin aspect instance
37
* @param aspectClass the aspect class
38
* @param perTypeWithin the class for which to search for an aspect instance
39
* @return the associated aspect instance
40
* @throws NoAspectBoundException if no such aspect, or no aspect bound
41
*/
42
public static <T> T aspectOf(Class<T> aspectClass, Class<?> perTypeWithin) throws NoAspectBoundException;
43
44
/**
45
* Checks if singleton aspect or percflow / percflowbelow aspect is bound
46
* @param aspectClass the aspect class
47
* @return true if singleton aspect or percflow / percflowbelow aspect is bound
48
*/
49
public static boolean hasAspect(Class<?> aspectClass);
50
51
/**
52
* Checks if the perthis / pertarget aspect is bound
53
* @param aspectClass the aspect class
54
* @param perObject the this/target object for which to look for an aspect instance
55
* @return true if the perthis / pertarget aspect is bound
56
*/
57
public static boolean hasAspect(Class<?> aspectClass, Object perObject);
58
59
/**
60
* Checks if the pertypewithin aspect is bound
61
* @param aspectClass the aspect class
62
* @param perTypeWithin class
63
* @return true if the pertypewithin aspect is bound
64
*/
65
public static boolean hasAspect(Class<?> aspectClass, Class<?> perTypeWithin);
66
}
67
```
68
69
**Usage Examples:**
70
71
```java
72
import org.aspectj.lang.Aspects;
73
import org.aspectj.lang.NoAspectBoundException;
74
75
// Singleton aspect access
76
@Aspect
77
public class LoggingAspect {
78
private int callCount = 0;
79
80
@Before("execution(* com.example..*.*(..))")
81
public void logCall() {
82
callCount++;
83
System.out.println("Call #" + callCount);
84
}
85
86
public int getCallCount() {
87
return callCount;
88
}
89
}
90
91
public class AspectClient {
92
public void checkLoggingAspect() {
93
try {
94
if (Aspects.hasAspect(LoggingAspect.class)) {
95
LoggingAspect aspect = Aspects.aspectOf(LoggingAspect.class);
96
System.out.println("Total calls: " + aspect.getCallCount());
97
} else {
98
System.out.println("Logging aspect is not bound");
99
}
100
} catch (NoAspectBoundException e) {
101
System.out.println("Failed to access aspect: " + e.getMessage());
102
}
103
}
104
}
105
```
106
107
### Per-Object Aspect Management
108
109
For aspects with `perthis()` or `pertarget()` instantiation, each target object gets its own aspect instance:
110
111
```java
112
// Per-object aspect example
113
@Aspect("perthis(execution(* com.example.service.*.*(..)))")
114
public class ServiceMonitorAspect {
115
private long startTime;
116
private int methodCount = 0;
117
118
@Before("execution(* com.example.service.*.*(..))")
119
public void startTiming() {
120
if (methodCount == 0) {
121
startTime = System.currentTimeMillis();
122
}
123
methodCount++;
124
}
125
126
public long getElapsedTime() {
127
return System.currentTimeMillis() - startTime;
128
}
129
130
public int getMethodCount() {
131
return methodCount;
132
}
133
}
134
135
public class ServiceClient {
136
public void monitorService(Object serviceInstance) {
137
try {
138
if (Aspects.hasAspect(ServiceMonitorAspect.class, serviceInstance)) {
139
ServiceMonitorAspect monitor =
140
Aspects.aspectOf(ServiceMonitorAspect.class, serviceInstance);
141
System.out.println("Service has been running for " +
142
monitor.getElapsedTime() + " ms");
143
System.out.println("Methods called: " + monitor.getMethodCount());
144
}
145
} catch (NoAspectBoundException e) {
146
System.out.println("No monitor bound to this service instance");
147
}
148
}
149
}
150
```
151
152
### Per-Type-Within Aspect Management
153
154
For aspects with `pertypewithin()` instantiation, each type within the specified type pattern gets its own aspect instance:
155
156
```java
157
// Per-type-within aspect example
158
@Aspect("pertypewithin(com.example.model.*)")
159
public class EntityAuditAspect {
160
private final String entityType;
161
private int operationCount = 0;
162
163
public EntityAuditAspect() {
164
// The entity type is determined at aspect creation time
165
this.entityType = thisJoinPoint.getSignature().getDeclaringType().getSimpleName();
166
}
167
168
@Before("execution(* set*(..))")
169
public void auditPropertyChange() {
170
operationCount++;
171
System.out.println("Property change in " + entityType +
172
" (operation #" + operationCount + ")");
173
}
174
175
public int getOperationCount() {
176
return operationCount;
177
}
178
}
179
180
public class EntityAuditClient {
181
public void checkEntityAudit(Class<?> entityClass) {
182
try {
183
if (Aspects.hasAspect(EntityAuditAspect.class, entityClass)) {
184
EntityAuditAspect audit =
185
Aspects.aspectOf(EntityAuditAspect.class, entityClass);
186
System.out.println("Audit operations for " + entityClass.getSimpleName() +
187
": " + audit.getOperationCount());
188
}
189
} catch (NoAspectBoundException e) {
190
System.out.println("No audit aspect for " + entityClass.getSimpleName());
191
}
192
}
193
}
194
```
195
196
### Exception Handling
197
198
The aspect management API uses `NoAspectBoundException` to indicate when aspects are not available:
199
200
```java { .api }
201
/**
202
* Thrown by the aspectOf special method on aspect types
203
* when there is no aspect of that type currently bound.
204
*/
205
public class NoAspectBoundException extends RuntimeException {
206
/**
207
* Constructs exception with aspect name and optional cause
208
* @param aspectName name of the aspect that is not bound
209
* @param inner the underlying cause, if any
210
*/
211
public NoAspectBoundException(String aspectName, Throwable inner);
212
213
/**
214
* Default constructor for cases where no specific information is available
215
*/
216
public NoAspectBoundException();
217
218
/**
219
* Returns the underlying cause of this exception
220
* @return the cause, or null if there is no cause
221
*/
222
public Throwable getCause();
223
}
224
```
225
226
**Usage Examples:**
227
228
```java
229
public class RobustAspectClient {
230
231
public <T> T safeAspectAccess(Class<T> aspectClass) {
232
try {
233
return Aspects.aspectOf(aspectClass);
234
} catch (NoAspectBoundException e) {
235
System.err.println("Aspect " + aspectClass.getSimpleName() +
236
" is not bound: " + e.getMessage());
237
if (e.getCause() != null) {
238
System.err.println("Underlying cause: " + e.getCause().getMessage());
239
}
240
return null;
241
}
242
}
243
244
public void conditionalAspectUsage(Object target) {
245
// Check before attempting access
246
if (Aspects.hasAspect(ServiceMonitorAspect.class, target)) {
247
try {
248
ServiceMonitorAspect monitor =
249
Aspects.aspectOf(ServiceMonitorAspect.class, target);
250
// Use the aspect safely
251
System.out.println("Monitor found: " + monitor.getMethodCount() + " calls");
252
} catch (NoAspectBoundException e) {
253
// This should rarely happen if hasAspect returned true
254
System.err.println("Race condition: aspect became unbound");
255
}
256
}
257
}
258
}
259
```
260
261
## Aspect Instantiation Models
262
263
AspectJ supports several instantiation models that determine when and how aspect instances are created:
264
265
### Singleton (Default)
266
267
- **Pattern**: `@Aspect` or `@Aspect("")`
268
- **Behavior**: One aspect instance per JVM
269
- **Access**: `Aspects.aspectOf(AspectClass.class)`
270
- **Use Case**: Global cross-cutting concerns like logging, security
271
272
### Per-This
273
274
- **Pattern**: `@Aspect("perthis(pointcut)")`
275
- **Behavior**: One aspect instance per object matched by `this()`
276
- **Access**: `Aspects.aspectOf(AspectClass.class, thisObject)`
277
- **Use Case**: State tracking per object instance
278
279
### Per-Target
280
281
- **Pattern**: `@Aspect("pertarget(pointcut)")`
282
- **Behavior**: One aspect instance per object matched by `target()`
283
- **Access**: `Aspects.aspectOf(AspectClass.class, targetObject)`
284
- **Use Case**: Monitoring or decorating target objects
285
286
### Per-Type-Within
287
288
- **Pattern**: `@Aspect("pertypewithin(TypePattern)")`
289
- **Behavior**: One aspect instance per type matching the pattern
290
- **Access**: `Aspects.aspectOf(AspectClass.class, matchingClass)`
291
- **Use Case**: Per-class statistics or type-specific behavior
292
293
### Per-Control-Flow
294
295
- **Pattern**: `@Aspect("percflow(pointcut)")` or `@Aspect("percflowbelow(pointcut)")`
296
- **Behavior**: One aspect instance per control flow thread
297
- **Access**: `Aspects.aspectOf(AspectClass.class)` (thread-local)
298
- **Use Case**: Request tracking, transaction management
299
300
## Performance Considerations
301
302
- **Compilation vs. Runtime**: For better performance, compile aspects with `ajc` and use them as binary dependencies rather than relying on load-time weaving
303
- **Reflection Overhead**: The `Aspects` class uses reflection internally, so direct aspect access (when available) is more efficient
304
- **Instance Checking**: Use `hasAspect()` before `aspectOf()` to avoid exceptions in conditional access scenarios
305
- **Thread Safety**: Singleton aspects should be thread-safe; per-object aspects are naturally isolated