前言
本章節(jié)目的不是詳細(xì)的介紹AspectJ的細(xì)節(jié)况褪,而是最近項(xiàng)目用到了AspectJ,通過(guò)一個(gè)簡(jiǎn)單例子來(lái)看下定義切片以及使用切片的流程是怎樣的寒砖。
AspectJ
AspectJ 是使用最為廣泛的 AOP 實(shí)現(xiàn)方案逗宁,適用于 Java 平臺(tái),官網(wǎng)地址:http://www.eclipse.org/aspectj/ 嫉到。AspectJ 是在靜態(tài)織入代碼沃暗,即在編譯期注入代碼的。
-
AspectJ 提供了一套全新的語(yǔ)法實(shí)現(xiàn)屯碴,完全兼容 Java(跟 Java 之間的區(qū)別描睦,只是多了一些關(guān)鍵詞而已)。同時(shí)导而,還提供了純 Java 語(yǔ)言的實(shí)現(xiàn)忱叭,通過(guò)注解的方式隔崎,完成代碼編織的功能。因此我們?cè)谑褂?AspectJ 的時(shí)候有以下兩種方式:
使用 AspectJ 的語(yǔ)言進(jìn)行開(kāi)發(fā)
通過(guò) AspectJ 提供的注解在 Java 語(yǔ)言上開(kāi)發(fā)
因?yàn)樽罱K的目的其實(shí)都是需要在字節(jié)碼文件中織入我們自己定義的切面代碼韵丑,不管使用哪種方式接入 AspectJ爵卒,都需要使用 AspectJ 提供的代碼編譯工具 ajc 進(jìn)行編譯。
在 Android Studio 上一般使用注解的方式使用 AspectJ撵彻,因?yàn)?Android Studio 沒(méi)有 AspectJ 插件钓株,無(wú)法識(shí)別 AspectJ 的語(yǔ)法(不過(guò)在 Intellij IDEA 收費(fèi)版上可以使用 AspectJ 插件),所以后面的語(yǔ)法說(shuō)明和示例都是以注解的實(shí)現(xiàn)方式陌僵。
常用術(shù)語(yǔ)
在了解AspectJ的具體使用之前轴合,先了解一下其中的一些基本的術(shù)語(yǔ)概念,這有利于我們掌握AspectJ的使用以及AOP的編程思想碗短。
JoinPoints
JoinPoints(連接點(diǎn))受葛,程序中可能作為代碼注入目標(biāo)的特定的點(diǎn)。在AspectJ中可以作為JoinPoints的地方包括:
PointCuts
PointCuts(切入點(diǎn))偎谁,其實(shí)就是代碼注入的位置总滩。與前面的JoinPoints不同的地方在于,其實(shí)PointCuts是有條件限定的JoinPoints巡雨。比如說(shuō)闰渔,在一個(gè)Java源文件中,會(huì)有很多的JoinPoints铐望,但是我們只希望對(duì)其中帶有@debug注解的地方才注入代碼冈涧。所以,PointCuts是通過(guò)語(yǔ)法標(biāo)準(zhǔn)給JoinPoints添加了篩選條件限定蝌以。
Advice
Advice(通知)炕舵,其實(shí)就是注入到class文件中的代碼片。典型的 Advice 類(lèi)型有 before跟畅、after 和 around咽筋,分別表示在目標(biāo)方法執(zhí)行之前、執(zhí)行后和完全替代目標(biāo)方法執(zhí)行的代碼徊件。
Aspect
Aspect(切面)奸攻,Pointcut 和 Advice 的組合看做切面。
Weaving
注入代碼(advices)到目標(biāo)位置(joint points)的過(guò)程
接下來(lái)通過(guò)項(xiàng)目看一下實(shí)踐過(guò)程
傳送門(mén):android-aop-samples
在annotation里定義注解
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface CheckLogin {
}
在android studio的android工程中使用AspectJ的時(shí)候虱痕,我們需要在項(xiàng)目的build.gradle的文件中添加一些配置:
dependencies {
classpath 'org.aspectj:aspectjtools:1.8.9'
...
}
在新建module里定義AspectjPlugin睹耐,也可以直接寫(xiě)到gradle里面,固定寫(xiě)法沒(méi)啥說(shuō)的
public class AspectjPlugin implements Plugin<Project> {
void apply(Project project) {
project.dependencies {
compile 'org.aspectj:aspectjrt:1.8.9'
}
final def log = project.logger
log.error "========================";
log.error "Aspectj切片開(kāi)始編織Class!";
log.error "========================";
project.android.applicationVariants.all { variant ->
def 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;
}
}
}
}
}
}
在app的build.gradle里面
import com.app.plugin.AspectjPlugin
apply plugin: AspectjPlugin
定義切片
@Aspect
public class CheckLoginAspect {
@Pointcut("execution(@com.app.annotation.aspect.CheckLogin * *(..))")//方法切入點(diǎn)
public void methodAnnotated() {
}
@Around("methodAnnotated()")//在連接點(diǎn)進(jìn)行方法替換
public void aroundJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
if (!SharedPreferenceUtil.isLogin()) {
Snackbar.make(AopApplication.getAppContext().getCurActivity().getWindow().getDecorView(), "請(qǐng)先登錄!", Snackbar.LENGTH_LONG)
.setAction("登錄", new View.OnClickListener() {
@Override
public void onClick(View view) {
SharedPreferenceUtil.setLogin(AopApplication.getAppContext(), true);
Toast.makeText(AopApplication.getAppContext(), "登錄成功", Toast.LENGTH_SHORT).show();
}
}).show();
return;
}
joinPoint.proceed();//執(zhí)行原方法
}
}
在MainActivity里面使用注解@CheckLogin部翘,看下build/intermediates/classes編譯出來(lái)的class里面的插入代碼
@CheckLogin
public void doMarkDown()
{
JoinPoint localJoinPoint = Factory.makeJP(ajc$tjp_0, this, this);Object[] arrayOfObject = new
Object[2];arrayOfObject[0] = this;arrayOfObject[1] =
localJoinPoint;CheckLoginAspect.aspectOf().aroundJoinPoint(new
MainActivity.AjcClosure1(arrayOfObject).linkClosureAndJoinPoint(69648));
}
static final void doMarkDown_aroundBody0(MainActivity ajc$this, JoinPoint paramJoinPoint)
{
Toast.makeText(AopApplication.getAppContext(), , 1).show();
}
使用總結(jié)
1.定義注解
2.添加入口plugin或者直接寫(xiě)在gradle里
3.定義切片硝训,設(shè)置@Pointcut使用execution來(lái)設(shè)置方法的切入點(diǎn)為com.app.annotation.aspect包下的CheckLogin
4.編寫(xiě)切片處理邏輯在Advice里,Advice就是我們插入的代碼可以以何種方式插入,有Before 還有 After窖梁、Around
5.在項(xiàng)目里使用切片赘风,達(dá)到在指定位置插入代碼的目的,可以在具體項(xiàng)目里面同一種場(chǎng)景使用該注解達(dá)到處理切面的問(wèn)題纵刘,大大減少了代碼的書(shū)寫(xiě)邀窃,更加是AOP的具體體現(xiàn),對(duì)OOP的一種彌補(bǔ)
最后
本章節(jié)只是介紹了少部分的AspectJ的使用假哎,還是那句老話瞬捕,AspectJ本身并沒(méi)有技術(shù)難點(diǎn),難的是怎么設(shè)計(jì)出好用的切面舵抹,無(wú)論是log還是監(jiān)控日志都可以使用該方式進(jìn)行嘗試.