Aspect Object Programming

Introduction

Aspect-Oriented Programming (AOP) complements Object-Oriented Programming (OOP) by providing another way of thinking about program structure. The key unit of modularity in OOP is the class, whereas in AOP the unit of modularity is the aspect. Aspects enable the modularization of concerns such as transaction management that cut across multiple types and objects. (Such concerns are often termed crosscutting concerns in AOP literature)

AOP concepts

Types of advice:

Bearcat supports Before advice, After returning advice and Around advice, since throwing an exception is not a best practise in Node.js.

Declaring an aspect

In Bearcat, an aspect is also a simple javaScript object, it is treated the same as other javaScript objects managed by Bearcat, so you can inject other beans into an aspect quite easily.
In an aspect, you should define advices, you can refer to declaring an advice part

Declaring an advice

advice is a function in an aspect, advice function’s last argument must be a next callback function to tell the AOP framework the end of the current advice execution.
In configuration metadata, you can use the advice attribute to specify the name of the advice in the current aspect.

1
"advice": "doBefore"

Before advice

1
2
3
4
Aspect.prototype.doBefore = function(next) {
console.log('Aspect doBefore');
next();
}

After advice

after advice is equal to after returning advice in Bearcat.
the joint point method execution callback return arguments will be passed to after advice.

1
2
3
4
Aspect.prototype.doAfter = function(err, r, next) {
console.log('Aspect doAfter ' + r);
next();
}

Around advice

in around advice, target object and target method will be passed as arguments.

1
2
3
4
5
6
7
Aspect.prototype.doAround = function(target, method, next) {
console.log('Aspect doAround before');
target[method](function(err, r) {
console.log('Aspect doAround after ' + r);
next(err, r + 100);
});
}

Declaring a pointcut

Recall that pointcuts determine join points of interest, and thus enable us to control when advice executes. In Bearcat, a pointcut declaration has two parts: a prefix declaring the type of advice, and a pointcut expression that determines exactly which method executions we are interested in.
Pointcut is declared in configuration metadata under the pointcut attribute

1
"pointcut": "before:.*?runBefore"

the prefix should be before, after and around corresponding to before advice, after advice and around advice.

after the prefix it is a : separator.

after the separator, it is the pointcut expression which matches the target method
the pointcut expression is actually a regexp expression
the target method has the following signature:

1
id.method

id is the bean id of the target
method is the method name of the target

therefore, when target object is a bean named with id car and has the method named runBefore
the following pointcut expression will matches:

1
"pointcut": "before:.*?runBefore"

Runtime support

when an advice is defined to be runtime, target method arguments will be passed to this advice.
before advice and around advice can be defined to be runtime, while after advice is actually runtime.
To use this feature, you can use the runtime attribute and set to be true

1
"runtime": true

before advice (runtime)

1
2
3
4
Aspect.prototype.doBeforeRuntime = function(num, next) {
console.log('Aspect doBeforeRuntime ' + num);
next();
}

around advice (runtime)

1
2
3
4
5
6
7
Aspect.prototype.doAroundRuntime = function(target, method, num, next) {
console.log('Aspect doAroundRuntime before ' + num);
target[method](num, function(err, r) {
console.log('Aspect doAroundRuntime after ' + r);
next(err, r + 100);
});
}

Embeded aspect configuration metas

Aspect is also a bean, to enable $ annotation, needs to configure in constructor as follows:

1
this.$aop = true;

Every method in Aspect’s prototype can be an advice, to be an advice, you just declare the pointcut like this:

1
var $pointcut = "pointcut expression";

order, runtime can also be declared as follows:

1
2
var $order = 1;
var $runtime = true;

a simple aspect example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var Aspect = function() {
this.$id = "aspect";
this.$aop = true;
}
Aspect.prototype.doBefore = function(next) {
var $pointcut = "before:.*?runBefore";
var $order = 10;
console.log('Aspect doBefore');
next();
}
module.exports = Aspect;

aop_annoation examples