Aspect Oriented Programming

Download Report

Transcript Aspect Oriented Programming

Aspect Oriented
Programming
Michael Kucera
CAS 706
March 15, 2005
Motivation for AOP
• Typical programs often have behavior that
doesn't fit into a single module.
• “Code Tangling”
Accounting
XML Parsing
Logging
Database
Crosscutting Concerns
• AOP addresses behaviors that span many, often
unrelated, modules.
• Core Concerns:
– Primary core functionality.
– Central functionality of a module.
• Crosscutting Concerns:
– System wide concerns that span multiple modules.
– Cuts across the typical division of responsibility.
• OOP creates a coupling between core and
crosscutting concerns.
• AOP aims to modularize crosscutting concerns.
Aspects
• In AOP crosscutting concerns are implemented
in aspects instead of fusing them into core
modules.
• Aspects are an additional unit of modularity.
• Aspects can be reused.
• By reducing code tangling it makes it easier to
understand what the core functionality of a
module is.
• An “aspect weaver” takes the aspects and the
core modules and composes the final system.
AspectJ Example
public class Example {
public static void deliverMessage(String message) {
System.out.println("The message is: " + message);
}
public static void main(String [] args) {
deliverMessage("I'm here");
deliverMessage("AspectJ rocks");
}
}
public aspect ExampleAspect {
pointcut helloPC() : call(void Example.deliverMessage(..));
before() : helloPC() {
System.out.print("Hello! ");
}
after() : helloPC() {
System.out.println("The message has been delivered.");
}
}
Output:
Hello! The message is: I'm here
The message has been delivered.
Hello! The message is: AspectJ rocks
The message has been delivered.
Weaving
• Weaving rules specify how to integrate the final
system.
• Can be implemented in various ways:
– Source to source translation.
– Bytecode enhancement, first compile source with
original compiler, then weave aspects into class files.
– Just-in-time weaving done by a classloader.
– By the language compiler.
• The JAsCo language supports runtime weaving
and unweaving.
AspectJ – Overview
• Join point
– Places where crosscutting concerns can be woven in.
– Ex. method call, field access, object creation.
• Pointcut
– Declaration that selects join points and collects
contexts at that point.
• Advice
– Code to be executed at a join point.
– Three types: before, after and around.
• Aspect
– Unit of modularity.
– Can contain fields and methods like a regular Java
class.
AspectJ – Overview
• Introduction
– Introduces changes to classes.
– Makes static changes that alone do not effect be
behavior of the system.
– Can add methods and fields to a class.
– Add implemented interfaces.
– Adds mixin like inheritance to Java.
• Compile time declaration
– Add compile time errors and warnings upon detecting
certain usage patterns.
• Every valid Java program is a valid AspectJ
program.
AspectJ – Join point model
• Any identifiable execution point:
–
–
–
–
–
method body
method call
constructor body
constructor call
field access
–
–
–
–
–
catch block
class (static) initialization
object initialization
object pre-initialization
advice execution
• All join points have a context. Certain pointcuts
can capture the context and pass it to the
advice.
AspectJ – Capturing Context
public class Employee {
int salary;
public void setSalary(int salary) {
this.salary = salary;
}
public static void main(String[] args) {
Employee emp = new Employee();
emp.setSalary(50000);
System.out.println("Salary: " + emp.salary);
}
}
public aspect MoneyAspect {
pointcut employeePC(int salary) : call(* Employee.setSalary(..)) && args(salary) ;
void around(int salary) : employeePC(salary) {
salary *= 2;
proceed(salary);
}
}
Output:
Salary: 100000
What about encapsulation?
• AOP breaks encapsulation in a controlled
manner.
• Encapsulation is broken between classes and
aspects.
• Aspects marked as privileged have access to
the private members of a class.
• Encapsulation is preserved between classes.
• An aspect encapsulates a crosscutting concern.
AspectJ - Pointcuts
• Can be named or anonymous.
• Named pointcuts:
[access-specifier] pointcut pointcut-name( [args] ) : pointcut-definition ;
• The pointcut-definition part consists of
anonymous pointcuts.
• Anonymous pointcut used in advice definition:
before() : call(* Account.*(..)) {
... advice body
}
AspectJ - Pointcuts
• Pointcuts can use combinations of other
pointcuts.
pointcut employeeCalls() : call(* Employee.*(..));
pointcut internalOperations() : employeeCalls() &&
within(banking..*);
• Wildcards can be used to capture join points that
share common characteristics.
* denotes any number of characters except a period
.. denotes any number of characters
+ denotes any subclass of a given type
• Can use operators &&, || and ! within a pointcut
definition.
AspectJ – Kinded pointcuts
• Kinded pointcuts capture exposed join points.
Join point category
Pointcut syntax
Method execution
execution(MethodSignature)
Method call
call(MethodSignature)
Constructor execution
execution(ConstructorSignature)
Constructor call
call(ConstructorSignature)
Class initialization
staticinitialization(TypeSignature)
Field read access
get(FieldSignature)
Field write access
set(FieldSignature)
Exception handler execution
handler(TypeSignature)
Object initialization
initialization(ConstructorSignature)
Object pre-initialization
preinitialization(ConstructorSignature)
Advice execution
adviceexecution()
AspectJ – Control-flow based pointcuts
• Capture all the join points within the control flow
of a join point.
• cflow(call(* Account.debit(..)))
– All the join points within the control flow of any debit
method in the Account class, including the call to
debit().
• cflowbelow(call(* Account.debit(..)))
– Same as above but not including the call to debit().
• cflowbelow can be used to select non-recursive
calls.
AspectJ – Pointcuts that capture context
• this()
– the current object
• target()
– target object of a method call
• args()
– arguments passed to a method or constructor
– exception caught by a handler join point
– new value to be set in a field write access
this.obj.doSomething(val)
this()
target()
args()
AspectJ – Advice
• Advice is a method like construct that expresses
the action to be taken at the join points that are
captured by a pointcut.
• Before advice
– executes prior to the join point
• After advice
– executes following the join point
• Around advice
– surrounds the join point’s execution
– can continue original execution, bypass execution or
cause execution with an altered context
– can cause execution of the join point multiple times
AspectJ – Around advice
public aspect AccountAspect {
void around(Account account, float amount) :
call(* Account.withdraw(float)) &&
target(account) &&
args(amount) {
System.out.println("Before withdrawl of amount: " + amount);
if(amount > account.getBalance())
System.out.println("Cannot make withdrawl");
else {
proceed(account, amount);
System.out.println("Withdrawl successful, balance: " +
account.getBalance());
}
}
}
AspectJ – After advice
• Executes after the execution of a join point.
• Can distinguish between normal returns and
those that throw an exception.
after() returning : call(* Account.*(..)) {
System.out.println(“Successful return”);
}
after() throwing : call(* Account.*(..)) {
System.out.println(“Failure”);
}
after() throwing(RemoteException ex) : call(* *.*(..)) {
System.out.println(“Exception “ + ex +
“ while executing “ + thisJoinPoint);
}
AspectJ – Method result caching
public class Factorial {
public static long factorial(int n) {
if(n == 0) return 1;
return n * factorial(n - 1);
}
public static void main(String[] args) {
System.out.println("Result: " + factorial(5));
System.out.println("Result: " + factorial(5));
}
}
public aspect FactorialOptimizeAspect {
private Map cache = new HashMap();
pointcut factorialOp(int n) : call(long *.factorial(int)) && args(n);
long around(int n) : factorialOp(n) {
Object value = cache.get(new Integer(n));
if(value == null)
return proceed(n);
System.out.println(value + " retrieved from cache");
return ((Long)value).longValue();
}
after(int n) returning(long result) : factorialOp(n) {
if(cache.put(new Integer(n), new Long(result)) == null)
System.out.println(result + " added to cache");
}
}
Output:
1 added to cache
1 added to cache
2 added to cache
6 added to cache
24 added to cache
120 added to cache
Result: 120
120 retrieved from cache
Result: 120
AspectJ – Aspects
• Aspects can:
–
–
–
–
–
Include data members and methods.
Be declared abstract (won’t be weaved).
Have access specifiers.
Extend classes or aspects.
Implement interfaces.
• Aspects are not the same as classes:
– Cannot be directly instantiated.
– Cannot inherit from concrete aspects.
– Can be marked as privileged.
AspectJ – Abstract aspect example
public abstract aspect AbstractLogging {
public pointcut logPoints() : call(* *.*(..));
public abstract Logger getLogger();
before() : logPoints() {
getLogger().log(Level.INFO, thisJoinPoint.toString());
}
}
public aspect FactorialLoggingAspect extends AbstractLogging {
// pointcut override
public pointcut logPoints() : call(* *.factorial(..));
public Logger getLogger() {
return Logger.global;
}
}
Static crosscutting
• Dynamic crosscutting modifies the execution
behavior of the program.
• Static crosscutting modifies the structure of the
program.
–
–
–
–
Member introduction.
Type-hierarchy modification.
Compile-time warning declaration.
Exception softening.
• Member introduction adds data members and
methods to classes.
Introduction – Mixin style inheritance
public interface Tagable {
void setTag(String tag);
String getTag();
}
public aspect TagAspect {
private String Tagable.tag;
public String Tagable.getTag() {
return tag;
}
public void Tagable.setTag(String tag) {
this.tag = tag;
}
declare parents : Employee implements Tagable;
before(Tagable t) : target(t) && !within(TagAspect) {
t.setTag(thisJoinPoint.toString());
}
after(Tagable t) : target(t) && !within(TagAspect){
System.out.println(t.getTag());
}
}
Default interface implementation
public interface Tagable {
public void setTag(String tag);
public String getTag();
static aspect Implementation {
private String Tagable.tag;
public void Tagable.setName(String tag) {
this.tag = tag;
}
public String Tagable.getName() {
return tag;
}
}
}
public class Example implements Tagable {
public static void main(String[] args) {
Example ex = new Example();
ex.setTag(”It works”);
System.out.println(”Tag: ” + ex.getTag());
}
}
Modifying the class hierarchy
• Existing classes can be declared to implement
an interface or extend a superclass.
• Works as long as Java inheritance rules are not
violated (no multiple inheritance).
declare parents : [Type] implements [InterfaceList];
declare parents : [Type] extends [Class];
• Aspects can be made dependant only on a base
type or interface. This makes aspects more
reusable.
AspectJ – Exception softening
• Converts a checked exception into a runtime
exception.
• Sometimes it can be inconvenient to have to
deal with checked exceptions. Involves a
proliferation of try/catch blocks and throws
clauses. Example: SQLException in JDBC API
declare soft : SQLException : within(DatabaseAccess);
• Exception is automatically rethrown as a
org.aspectj.lang.SoftException
Policy Enforcement
• Mechanism for ensuring that system components
follow certain programming practices.
• For example; enforce that public access to
instance variables is prohibited.
• Avoid incorrect usages of an API.
• Ensures better quality.
• In AOP policy enforcement concerns can be
implemented in aspects.
• Aspect based policy enforcement is reusable.
• AOP can be used for “Design by Contract”.
Policy Enforcement in AspectJ
• Compile time and runtime enforcement.
• In AspectJ it is possible to specify custom
compile time warnings and errors using
pointcuts.
declare warning : get(* System.out) || get(* System.err)
: “consider using Logger.log() instead”;
declare error : set(public * *) || get(public * *)
: “nonpublic access is not allowed”;
• Runtime enforcement can be achieved through
advice that detects policy violations.
Aspect precedence
• It is possible for more than one piece of advice
to apply to a join point.
• It may be important to control the order in which
advice is applied.
declare precedence : Aspect1, Aspect2, ... ;
• In the absence of any specified precedence
control, the order in which the advice is applied
is non-deterministic (at compile time).
• If more than one piece of advice in the same
aspect applies to a pointcut, then the advice that
comes first in the file has precedence.
Aspect association
• Typically an aspect represents a singleton object
(aspect instance) that is instantiated by the
system.
• It may be useful to have more than one instance
of an aspect in the system. Can create
associations between aspect instances and
other objects.
• Can associate a new aspect instance with a
target object by using a pointcut.
public aspect CacheAspect perthis(access()) { .. }
• Alternatively can use introduction.
CLOS – Method combination
• Applicable generic methods are combined into a
single effective method.
(defclass food () ())
(defclass pie (food) ())
(defmethod cook ((p pie))
(print "Cooking a pie."))
(defmethod cook :before ((f food))
(print "A food is about to be cooked."))
(defmethod cook :after ((f food))
(print "A food has been cooked."))
(defmethod cook :before ((p pie))
(print "A pie is about to be cooked."))
(defmethod cook :after ((p pie))
(print "A pie has been cooked."))
(setq pie-1 (make-instance 'pie))
(cook pie-1)
Output:
A pie is about to be cooked
A food is about to be cooked
Cooking a pie
A food has been cooked
A pie has been cooked
JAsCo – An aspect oriented language
• Emphasizes aspect reuse.
• In AspectJ pointcuts are hardcoded... less reuse.
• JAsCo allows adding, altering and removing
aspects while the program is running.
• Supports runtime weaving and unweaving.
• Aspect beans:
– Contain hooks: abstract pointcut definitions together
with advice
• Connectors:
– used to deploy reusable aspect beans
– explicitly specify the way aspect beans are combined
JAsCo – Stateful aspects
class LoggerBean {
hook LogHook {
LogHook(startmethod(..args1), runningmethod(..args2), stopmethod(..args3)) {
start > pc1;
pc1: execution(startmethod)
> pc3 || pc2;
pc3: execution(stopmethod)
> pc1;
pc2: execution(runningmethod) > pc3 || pc2;
}
before pc2 (){
System.out.println(”executing: ” + calledmethod.getName());
}
}
}
static connector testconnector {
LoggerBean.LogHook hook1
= new LoggerBean.LogHook(* *.*.start(*), * *.doIt(*), * *.*.stop(*));
LoggerBean.LogHook hook2
= new LoggerBean.LogHook(* *.*.login(*), * *.shop(*), * *.*.logout(*));
hook1.before();
hook2.before();
}
// optional
Case study – JDO
• JDO (Java Data Objects) is a “transparent
persistence” framework for Java.
• Allows the manipulation of the state of an object
to transparently update a database.
Employee
public class Employee {
string name;
int salary;
}
fred.salary = 200;
Name
Salary
Fred
100 200
Biff
150
causes transparent
update
Case study – JDO
• Uses advice to add side effects to field access.
• Weaving is done by the JDO bytecode enhancer.
Employee.java
javac
Employee.class
mapping.xml
JDO bytecode
enhancer
(Aspect weaver)
enhanced
Employee.class
Conclusion
•
•
•
•
•
•
•
•
•
AOP has many potential benefits.
Higher modularization.
Cleaner responsibilities for individual modules.
Improved separation of concerns.
Easier system evolution. Much easier to add
crosscutting functionality.
More code reuse.
Can make code easier or harder to understand.
Already starting to become mainstream (JBoss,
JDO, Spring).
Like any new technology, AOP can be abused.
References
•
•
•
•
•
Laddad, Ramnivas. AspectJ in Action. Greenwich, Conn.: Manning
Publications, 2003.
Lesiecki, Nicholas. “Improve modularity with aspect oriented programming”.
IBM Developerworks, January,2002. Available online at http://www106.ibm.com/developerworks/java/library/j-aspectj.
Marshall, Joe. “Warp Speed Introduction to CLOS”. Available online at
http://home.comcast.net/~prunesquallor/guide.html.
Vanderperren, W., Suvee, D., Cibran, M. and De Fraine, B. Stateful Aspects
in JAsCo. In proceedings of SC 2005, LNCS, April 2005
Eclipse AspectJ plug-in, http://eclipse.org/aspectj.