.class ul { display: inline;}
#id p {font-style: italic;}
a:hover {color: #FF4630; }
input[type=text] {font-family: monospace;}
CSS + HTML
Slide 16
Slide 16 text
.class ul { display: inline;}
#id p {font-style: italic;}
a:hover {color: #FF4630; }
input[type=text] {font-family: monospace;}
CSS + HTML
Attributes
behaviors/appearance applied to...
markup nodes in the source code,
defined by...
Selectors
rules and patterns
Slide 17
Slide 17 text
Vocabulary
Advices
behaviors applied to...
JoinPoints
positions in the source code,
defined by...
PointCuts
rules and patterns
Attributes
behaviors/appearance applied to...
markup nodes in the source code,
defined by...
Selectors
rules and patterns
Slide 18
Slide 18 text
4.
Hello AOP
Let’s write an aspect
Slide 19
Slide 19 text
Bytecode
weaving
AspectJ
Slide 20
Slide 20 text
Hello World
@interface TraceLog {}
public class MyClass {
@TraceLog
public void foo() {
// ...
}
}
@Aspect
public class TraceLogAspect {
@Pointcut("execution(@TraceLog * *(..))")
public void traceLogMethod(){}
@Before("traceLogMethod())")
public void advice(JoinPoint jp) {
Log.v("AspectTag", "> " + jp.getSignature());
}
}
Slide 21
Slide 21 text
Hello World
@interface TraceLog {}
public class MyClass {
@TraceLog
public void foo() {
// ...
}
}
@Aspect
public class TraceLogAspect {
@Pointcut("execution(@TraceLog * *(..))")
public void traceLogMethod(){}
@Before("traceLogMethod())")
public void advice(JoinPoint jp) {
Log.v("AspectTag", "> " + jp.getSignature());
}
}
Slide 22
Slide 22 text
Hello World
@interface TraceLog {}
public class MyClass {
@TraceLog
public void foo() {
// ...
}
}
@Aspect
public class TraceLogAspect {
@Pointcut("execution(@TraceLog * *(..))")
public void traceLogMethod(){}
@Before("traceLogMethod())")
public void advice(JoinPoint jp) {
Log.v("AspectTag", "> " + jp.getSignature());
}
}
Slide 23
Slide 23 text
Hello World
@interface TraceLog {}
public class MyClass {
@TraceLog
public void foo() {
// ...
}
}
@Aspect
public class TraceLogAspect {
@Pointcut("execution(@TraceLog * *(..))")
public void traceLogMethod(){}
@Before("traceLogMethod())")
public void advice(JoinPoint jp) {
Log.v("AspectTag", "> " + jp.getSignature());
}
}
Slide 24
Slide 24 text
Hello World
@interface TraceLog {}
public class MyClass {
@TraceLog
public void foo() {
// ...
}
}
@Aspect
public class TraceLogAspect {
@Pointcut("execution(@TraceLog * *(..))")
public void traceLogMethod(){}
@Before("traceLogMethod())")
public void advice(JoinPoint jp) {
Log.v("AspectTag", "> " + jp.getSignature());
}
}
Slide 25
Slide 25 text
Hello World
@interface TraceLog {}
public class MyClass {
@TraceLog
public void foo() {
// ...
}
}
@Aspect
public class TraceLogAspect {
@Pointcut("execution(@TraceLog * *(..))")
public void traceLogMethod(){}
@Before("traceLogMethod())")
public void advice(JoinPoint jp) {
Log.v("AspectTag", "> " + jp.getSignature());
}
}
Slide 26
Slide 26 text
Hello World
@interface TraceLog {}
public class MyClass {
@TraceLog
public void foo() {
// ...
}
}
@Aspect
public class TraceLogAspect {
@Pointcut("execution(@TraceLog * *(..))")
public void traceLogMethod(){}
@Before("traceLogMethod())")
public void advice(JoinPoint jp) {
Log.v("AspectTag", "> " + jp.getSignature());
}
}
Slide 27
Slide 27 text
5.
Concrete examples
Beyond the logging “Hello world”
Slide 28
Slide 28 text
Performance Monitoring
@Aspect
public class PerformanceMonitorAspect {
@Pointcut("execution(@Monitor * *(..))")
public void monitoredMethod(){}
@Around("monitoredMethod())")
public Object advice(ProceedingJoinPoint pjp) {
long start = System.nanoTime();
Object result = pjp.proceed();
long end = System.nanoTime();
Log.i("Monitored", jp.toString + " : "
+ (end - start) + "ns");
return result;
}
}
Slide 29
Slide 29 text
Performance Monitoring
@Aspect
public class PerformanceMonitorAspect {
@Pointcut("execution(@Monitor * *(..))")
public void monitoredMethod(){}
@Around("monitoredMethod())")
public Object advice(ProceedingJoinPoint pjp) {
long start = System.nanoTime();
Object result = pjp.proceed();
long end = System.nanoTime();
Log.i("Monitored", jp.toString + " : "
+ (end - start) + "ns");
return result;
}
}
Slide 30
Slide 30 text
Performance Monitoring
@Aspect
public class PerformanceMonitorAspect {
@Pointcut("execution(@Monitor * *(..))")
public void monitoredMethod(){}
@Around("monitoredMethod())")
public Object advice(ProceedingJoinPoint pjp) {
long start = System.nanoTime();
Object result = pjp.proceed();
long end = System.nanoTime();
Log.i("Monitored", jp.toString + " : "
+ (end - start) + "ns");
return result;
}
}
Slide 31
Slide 31 text
Memoization
@Aspect
public class MemoizationAspect {
@Pointcut("execution(@Memoize * *(..))")
public void memoizedMethod() {}
@Around("memoizedMethod()")
public Object advice(ProceedingJoinPoint pjp) {
Object cached = getCached(pjp);
if (cached == null) {
cached = pjp.proceed();
cacheResult(pjp, cached);
}
return cached;
}
}
Validation
@interface Validation { String regexp() default ".*";
}
public class UserAccount {
@Validation(regexp = "[a-zA-Z]{6,}")
public void setUserName(String userName) {
mUserName = userName;
}
@Validation(regexp = "^(?=.*\d)(?=.*[A-Z]).{8,}$")
public void setPassword(String password) {
mPassword = password;
}
}
Slide 36
Slide 36 text
Validation
@interface Validation { String regexp() default ".*";
}
public class UserAccount {
@Validation(regexp = "[a-zA-Z]{6,}")
public void setUserName(String userName) {
mUserName = userName;
}
@Validation(regexp = "^(?=.*\d)(?=.*[A-Z]).{8,}$")
public void setPassword(String password) {
mPassword = password;
}
}
Slide 37
Slide 37 text
Validation
@Aspect
public class ValidationAspect {
@Before("execution(@Validation * *(..))
&& args(input) && @annotation(v)")
public void advice(JoinPoint jp,
String input,
Validation v) {
Pattern pattern = Pattern.compile(v.regexp());
Matcher matcher = pattern.matcher(input);
if (!matcher.matches()) {
throw new InvalidInputException();
}
}
}
Slide 38
Slide 38 text
Validation
@Aspect
public class ValidationAspect {
@Before("execution(@Validation * *(..))
&& args(input) && @annotation(v)")
public void advice(JoinPoint jp,
String input,
Validation v) {
Pattern pattern = Pattern.compile(v.regexp());
Matcher matcher = pattern.matcher(input);
if (!matcher.matches()) {
throw new InvalidInputException();
}
}
}
Slide 39
Slide 39 text
Validation
@Aspect
public class ValidationAspect {
@Before("execution(@Validation * *(..))
&& args(input) && @annotation(v)")
public void advice(JoinPoint jp,
String input,
Validation v) {
Pattern pattern = Pattern.compile(v.regexp());
Matcher matcher = pattern.matcher(input);
if (!matcher.matches()) {
throw new InvalidInputException();
}
}
}
Slide 40
Slide 40 text
What about the stack trace ?
com.example.InvalidInputException
at com.example.ValidationAspect.advice(ValidationAspect.java:35)
at com.example.MyClass.setPassword(UserAccount.java:83)
at com.example.test.UserAccount.onSubmit(UserAccount.java:42)
... 5 more
Slide 41
Slide 41 text
Undo / Redo
interface IUndoableAction {
public void undo();
public void redo();
}
class FieldAction implements IUndeoableAction {
Field mField; Object mTarget, mOldValue, mNewValue;
public FieldAction(Object target, Field field,
Object oldValue, Object newValue) {
mField = field;
mTarget = target;
mOldValue = oldValue;
mNewValue = newValue;
}
public void undo(){ field.set(target, oldValue); }
public void redo(){ field.set(target, newValue); }
}
Slide 42
Slide 42 text
Undo / Redo
@Aspect
public class UndoActionAspect {
@Pointcut("within(com.example.model.*)")
public void withinModelClass() {}
@Before("withinModelClass() && set(public * *)
&& args(newValue) && target(target)")
public void advice(JoinPoint jp,
Object target,
Object newValue) {
Field field = getField(jp, target);
Object oldValue = field.get(target);
UndoManager.getInstance()
.record(new FieldAction(target, field,
oldValue, newValue);
}
}
Slide 43
Slide 43 text
Gatekeeping
enum Role { DEV, BETA, PUBLIC }
@interface GateKeep { Role[] allowed(); }
public class HomeActivity {
@GateKeep(allowed = {DEV, BETA})
public void setupMaterialUI() {
// ...
}
@GateKeep(allowed = {DEV})
public void startJukeboxPlayer() {
// ...
}
}
Slide 44
Slide 44 text
Gatekeeping
@Aspect
public class GateKeepAspect {
@Around("execution(@GateKeep * *(..)) &&
@annotation(gk)")
public void advice(ProceedingJoinPoint jp,
Gatekeep gk) {
Role userRole = UserService.getInstance()
.getCurrentUser()
.getRole();
if (Arrays.asList(gate.allowedRoles())
.contains(userRole)) {
jp.proceed();
}
}
}
Slide 45
Slide 45 text
Multithreading
@Aspect
public class UIThreadAspect {
final Handler mMainHandler =
new Handler(Looper.
getMainLooper());
@Around("execution(@RunOnUIThread void *(..))")
public void advice(final ProceedingJoinPoint jp) {
if (Looper.myLooper() == Looper.getMainLooper())
{
jp.proceed();
} else {
mMainHandler.post(new Runnable() {
public void run() {
jp.proceed();
}
});
}
}
Slide 46
Slide 46 text
and more...
Retry failed request
Read/write locks
Boilerplate code
Slide 47
Slide 47 text
6.
More than just
advices
Slide 48
Slide 48 text
@Aspect
@DeclarePrecedence("around1, before2, after3,
around4, before5, after6")
public class OrderingAspect {
// Applied to the same pointcut, the result will be
// around1 {
// before2
// around4 {
// before5
// joinpoint
// after3
// }
// }
// after4
}
Ordering advices
Slide 49
Slide 49 text
Compile time checks
@Aspect
public class CoreCheckAspect {
@Pointcut("within(com.example.core.*)")
public void withinCorePackage() {}
@Pointcut("call(* com.example.ui..*..*(..))")
public void callToUIMethod() {}
@DeclareError("withinCorePackage() &&
callToUIMethod()")
static final String SEP_OF_CONCERNS =
"Core classes should not call UI methods";
}
Mixins
@Aspect
public class NamedAspect {
@DeclareMixin("com.example..*")
public static INamed makeNamed(Object namedObject)
{
return new NamedImpl(namedObject);
}
}
class Foo {
public void hello (){
Log.i("Hello", "My name is "
+ ((INamed) this).getName());
}
}
Slide 52
Slide 52 text
5.
General advices
no pun intended...
Slide 53
Slide 53 text
“
♬
“Forever in debt
to your priceless advice”
Nirvana
Slide 54
Slide 54 text
Things to remember
◎ Singleton by default
◎ Inheritance, abstracts, generics,
inner classes…
◎ Android flavors
◎ AOP is real programming
Slide 55
Slide 55 text
“
“It’s a kind of magic”
Queen
♬
Slide 56
Slide 56 text
Wizardry or Witchcraft
@Aspect
public class PreventNPEAspect {
@Pointcut("within(com.example.model.*)")
public void withinModelClass() {}
@Around("withinModelClass() && execution(* *(..))")
public void advice(ProceeJoinPoint jp) {
Object[] args = jp.getArgs();
for (int i = 0; i < args.length; i++) {
if (args[i] == null) {
args[i] = "";
}
}
jp.proceed(args);
}
}
Slide 57
Slide 57 text
Wizardry or Witchcraft
@Aspect
public class PreventNPEAspect {
@Around("execution(@PreventStringNPE * *(..))")
public void advice(ProceeJoinPoint jp) {
Object[] args = jp.getArgs();
for (int i = 0; i < args.length; i++) {
if (args[i] == null) {
args[i] = "";
}
}
jp.proceed(args);
}
}
Slide 58
Slide 58 text
Errors to avoid
◎ Pokeball : gotta catch’em all
Pointcuts must be as precise as possible
◎ Octopuss : arms reaching everywher
Pointcuts matching unrelated joinpoints
◎ Asp’ception : because aspect is code too
Pointcuts matching the advice itself
◎ Aspector Gadget : single responsibility
SOLID principles apply to AOP as well
Slide 59
Slide 59 text
Thanks ! Any question ?
We’re hiring
deezer.com/jobs
speakerdeck.com/xgouchet
Slide 60
Slide 60 text
A.
Addendum
Gradle script
Slide 61
Slide 61 text
The easy way
buildscript {
dependencies {
classpath 'com.uphyca.gradle:gradle-android-aspectj-plugin:
0.9.14'
}
}
apply plugin: 'com.uphyca.android-aspectj'
dependencies {
compile 'org.aspectj:aspectjrt:1.8.1'
}