380 likes | 629 Views
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.
E N D
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
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) args() this() target()
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(); // optional hook2.before(); }
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; causes transparent update
Case study – JDO • Uses advice to add side effects to field access. • Weaving is done by the JDO bytecode enhancer. javac Employee.java Employee.class JDO bytecode enhancer (Aspect weaver) mapping.xml 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://www-106.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.