我的理解AOP:就是代碼執(zhí)行的時候,他切入一個點聪富,讓代碼進入你寫好的代碼去執(zhí)行莺丑,然后在執(zhí)行切點代碼后面或者里面的代碼。
OOP是面向?qū)ο箝_發(fā),有邏輯對象抽象梢莽,功能高度獨立萧豆,都是已一個類或者方法的形式實現(xiàn)邏輯,但是耦合性太高,修改一個地方昏名,幾乎所有使用的地方都需要修改涮雷。
AOP:是面向切面編程,所謂的切面就是一個程序插入的入口轻局,正如上面所言洪鸭,在這個切面我做的操作都在一個地方實現(xiàn),當(dāng)所有的地方執(zhí)行到這個切面時候卿嘲,統(tǒng)一的邏輯到一個地方去執(zhí)行,那么就解決了夫壁,以后修改代碼的成本拾枣。其實也是spring DI和AOP2個最重要的內(nèi)容。
開始在Android上運用了盒让。
AOP 可以做很多東西梅肤,比如日志管理?
現(xiàn)在就做一個日志的例子。
1.首先在app.gradle 里面 在整個文件的最外層?添加如下代碼?
final def log = project.logger
final def variants = project.android.applicationVariants
variants.all { variant ->
if (!variant.buildType.isDebuggable()) {
log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
return;
}
JavaCompile javaCompile = variant.javaCompile
javaCompile.doLast {
String[] args = ["-showWeaveInfo",
"-1.8",
"-inpath", javaCompile.destinationDir.toString(),
"-aspectpath", javaCompile.classpath.asPath,
"-d", javaCompile.destinationDir.toString(),
"-classpath", javaCompile.classpath.asPath,
"-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]
log.debug"ajc args: " + Arrays.toString(args)
MessageHandler handler =new MessageHandler(true);
new Main().run(args, handler);
for (IMessage message : handler.getMessages(null,true)) {
switch (message.getKind()) {
case IMessage.ABORT:
case IMessage.ERROR:
case IMessage.FAIL:
log.error message.message, message.thrown
break;
case IMessage.WARNING:
log.warn message.message, message.thrown
break;
case IMessage.INFO:
log.info message.message, message.thrown
break;
case IMessage.DEBUG:
log.debug message.message, message.thrown
break;
}
}
}
}
2.在build.gradle里面添加一個aspectj的插件
dependencies {
classpath'org.aspectj:aspectjtools:1.8.9'
}
需求是這個的我們通過注解的方式在調(diào)用一個方法時候就注解下邑茄,我們調(diào)用了這個方法姨蝴,當(dāng)然你也可以在實現(xiàn)這個注解的方法里面干你想干的事情。
比如我們在隨便的一個頁面上調(diào)用這個initListeners方法時候注解下肺缕。
@BehaviorTrace(value ="dddd")
@Override
protected void initListeners() {
}
我們開始寫代碼
我們先寫一個注解類
@Target(ElementType.METHOD)//注解應(yīng)用在哪里
@Retention(RetentionPolicy.RUNTIME)//運行在哪里
// public?@interface 注解名稱{}
public @interface BehaviorTrace {
String value();
}
這個@@interface 是自定義注解的意思
value是我們自定義的屬性左医。
最終我們注解的樣子是這樣使用的
@BehaviorTrace(value ="xxxx")
寫完注解類后然后就是切面去切入這個注解類了
我們寫一個BeHaviorAspect 類,注意必須在類上面@Aspect 這個意思是標(biāo)注這個類是一個切面使用的類同木,在Android編譯時候浮梢,他會去匹配那些地方使用了切面,然后動態(tài)的生成代碼放入切入的代碼地方彤路。
@Aspect
public class BeHaviorAspect {
}
然后我們在這個類里面寫入代碼前秕硝,我們講究幾個概念
JPoint:代碼可注入的點,比如一個方法的調(diào)用處或者方法內(nèi)部洲尊、“讀远豺、寫”變量等。
Pointcut:用來描述 JPoint 注入點的一段表達式坞嘀,比如:調(diào)用 BehaviorTrace 類 fly 方法的地方躯护,call(* BehaviorTrace.fly(..))。
Advice:常見的有 Before丽涩、After榛做、Around 等,表示代碼執(zhí)行前、執(zhí)行后检眯、替換目標(biāo)代碼厘擂,也就是在 Pointcut 何處注入代碼。
Aspect:Pointcut 和 Advice 合在一起稱作 Aspect锰瘸。
我們寫一個切入的方法
//切點 標(biāo)簽注釋 這個類的 所有方法(所有參數(shù))
/**executeion 執(zhí)行在方法的內(nèi)部 call執(zhí)行在方法的外部 這個是注解的方式實現(xiàn)
* call 的調(diào)用方法的外面執(zhí)行
* execution是在調(diào)用的方法內(nèi)部執(zhí)行
* 這個是直接在調(diào)用一個類的方法時候切入 其實和注解方式的本質(zhì)是一樣的
**/
?? ?@Pointcut("execution(@com.yunsoft.shaoshupai.aop.BehaviorTrace * *(..))")
public void AnnoBehavor(){
}
這個AnnoBehavor方法刽严,相當(dāng)我們聲明了一個切入點,切入點是BehaviorTrace 的任何的方法調(diào)用時候 避凝,切入這個切點舞萄。
然后下面是處理這個切入的點的切入方法
//處理的是那個切點
// Before 是在這個點的前面
//Around 是返回的前面 或者??
//第二個參數(shù)是前面的切點方法名稱()
@Around("AnnoBehavor()")
public Object Deal(ProceedingJoinPoint point)throws Throwable{
//取得注解類型
MethodSignature methodSignature = (MethodSignature) point.getSignature();
//取得注解
BehaviorTrace behaviorTrace =? methodSignature.getMethod().getAnnotation(BehaviorTrace.class);
//取得注解內(nèi)容
? ? String contentType =? behaviorTrace.value();
//在執(zhí)行方法前
? ? long beagin=System.currentTimeMillis();
//方法執(zhí)行
? ? Object object =null;
try {
object =point.proceed();
}catch (Exception e){
}
//在執(zhí)行方法后
? ? long end=System.currentTimeMillis();
Log.e(TAG,contentType+"使用時間:? "+simpleDateFormat.format(end-beagin));
return object;
}
然后是前面
@Before("AnnoBehavor()")
public void excutMethod(JoinPoint joinPoint){
Log.e(TAG,"執(zhí)行方法前"+joinPoint.getTarget().toString()+"#"+joinPoint.getSignature().getName());
}
如果是后面
@after("AnnoBehavor()")
public void excutMethod(JoinPoint joinPoint){
Log.e(TAG,"執(zhí)行方法前"+joinPoint.getTarget().toString()+"#"+joinPoint.getSignature().getName());
}
在定義切點時候你也可以這樣定義
/**
* 在LoginSelectActivity的initViews調(diào)用的切點 可以是任意的方法
* call 的調(diào)用方法的外面執(zhí)行
* execution是在調(diào)用的方法內(nèi)部執(zhí)行
* 這個是直接在調(diào)用一個類的方法時候切入 其實和注解方式的本質(zhì)是一樣的
*/
@Pointcut("call(* xxxx.LoginSelectActivity.initViews(..))")
public void initViews(){
}