一依啰、什么是AOP
AOP是Aspect Oriented Programming的縮寫耕餐,即『面向切面編程』。它和我們平時接觸到的OOP都是編程的不同思想旺上,OOP瓶蚂,即『面向?qū)ο缶幊獭唬岢氖菍⒐δ苣K化宣吱,對象化窃这,而AOP的思想,則不太一樣凌节,它提倡的是針對同一類問題的統(tǒng)一處理钦听,通過預(yù)編譯方式和運行期動態(tài)代理實現(xiàn)程序功能的統(tǒng)一維護的一種技術(shù)。AOP是OOP的延續(xù)倍奢,是軟件開發(fā)中的一個熱點朴上,也是Spring框架中的一個重要內(nèi)容,是函數(shù)式編程的一種衍生范型卒煞。利用AOP可以對業(yè)務(wù)邏輯的各個部分進行隔離痪宰,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發(fā)的效率衣撬。
二乖订、AspectJ
適用于Aop的兼容java的一套語言
三、Aop基礎(chǔ)知識點
術(shù)語
通知具练、增強處理(Advice) 就是你想要的功能乍构。你給先定義好,然后再想用的地方用一下扛点。包含Aspect的一段處理代碼
連接點(JoinPoint)
所有可以被通知的地方哥遮,基本每個方法的錢、后(兩者都有也行)陵究,或拋出異常是時都可以是連接點眠饮,spring只支持方法連接點。其他如AspectJ還可以讓你在構(gòu)造器或?qū)傩宰⑷霑r都行铜邮,不過那不是咱們關(guān)注的仪召,只要記住,和方法有關(guān)的前前后后都是連接點松蒜。切入點(Pointcut) 假如你的一個類里扔茅,有15個方法,那就有十幾個連接點秸苗,但是你并不想在所有方法附件都使用通知(使用叫織入)咖摹,你只是想讓其中幾個,在調(diào)用這幾個方法之前难述、之后或者拋出異常時干點什么,那么就用切入點來定義這幾個方法吐句,讓切點來篩選連接點胁后,選中那幾個你想要的方法。
切面(Aspect) 切面是通知和切入點的結(jié)合∴率啵現(xiàn)在發(fā)現(xiàn)了吧攀芯,沒連接點什么事,鏈接點就是為了讓你好理解切點搞出來的文虏。
織入(weaving) 把切面應(yīng)用到目標對象來創(chuàng)建新的代理對象的過程侣诺。
Aop注解
@Aspect:聲明切面,標記類
@Pointcut(切點表達式):定義切點氧秘,規(guī)則定義
@Before(切點表達式):前置通知年鸳,切點之前執(zhí)行
@Around(切點表達式):環(huán)繞通知,切點前后執(zhí)行
@After(切點表達式):后置通知丸相,切點之后執(zhí)行
@AfterReturning(切點表達式):返回通知薪介,切點方法返回結(jié)果之后執(zhí)行
@AfterThrowing(切點表達式):異常通知拴测,切點拋出異常時執(zhí)行
1.@AfterThrowing通知與@AfterReturning通知是互斥的砌左,在同個切點上不可能同時出現(xiàn)浅蚪。
Aop切點表達式
基本模式
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
這里問號表示當前項可以有也可以沒有,其中各項的語義如下:
- modifiers-pattern:方法的可見性莱没,如public,protected;
- ret-type-pattern:方法的返回值類型华匾,如int,void等机隙;
- declaring-type-pattern:方法所在類的全路徑名蜘拉,如com.spring.Aspect;
- name-pattern:方法名類型黍瞧,如buisinessService()诸尽;
- param-pattern:方法的參數(shù)類型,如java.lang.String印颤;
- throws-pattern:方法拋出的異常類型您机,如java.lang.Exception;
eg
execution(public * com.spring.service.BusinessObject.businessService(java.lang.String,..))
上述切點表達式將會匹配使用public修飾年局,返回值為任意類型际看,并且是com.spring.BusinessObject類中名稱為businessService的方法,方法可以有多個參數(shù)矢否,但是第一個參數(shù)必須是java.lang.String類型的方法仲闽。
通配符:
- *通配符,該通配符主要用于匹配單個單詞僵朗,或者是以某個詞為前綴或后綴的單詞赖欣。
- ..通配符,該通配符表示0個或多個項验庙,主要用于declaring-type-pattern和param-pattern中顶吮,如果用于declaring-type-pattern中,則表示匹配當前包及其子包粪薛,如果用于param-pattern中悴了,則表示匹配0個或多個參數(shù)。
詳細參考:
Spring AOP切點表達式用法總結(jié)
四违寿、登陸案例
1.引入AspectJ
在build.gradle中的配置
apply plugin: 'com.android.application'
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.aspectj:aspectjtools:1.8.9'
classpath 'org.aspectj:aspectjweaver:1.8.9'
}
}
repositories {
mavenCentral()
}
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;
}
}
}
}
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.example.lcc.aoplogin"
minSdkVersion 15
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0-rc01'
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
compile 'org.aspectj:aspectjrt:1.8.9'
}
2.切面類
@Aspect
public class LoginAspect {
//定義篩選的規(guī)則湃交,所有添加了CheckLogin注解的方法都執(zhí)行邏輯
@Pointcut("execution(@com.example.lcc.aoplogin.annotation.CheckLogin * *(..))")
public void executionCheckLogin() {
}
/**
* 處理切面
* @param joinPoint
* @return
*/
@Around("executionCheckLogin()")
public Object checkLogin(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
CheckLogin checkLogin = signature.getMethod().getAnnotation(CheckLogin.class);
if (checkLogin != null) {
Context context = (Context) joinPoint.getThis();
if (MyApplication.isLogin) {
MyApplication.loginSdk.mLoginSuccess();
return joinPoint.proceed();
} else {
MyApplication.loginSdk.mLoginFail();
return null;
}
}
return joinPoint.proceed();
}
}
3.注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckLogin {
}
4.MyApp中,主要是處理不同登陸狀態(tài)下的邏輯
package com.example.lcc.aoplogin;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class MyApplication extends Application {
public static boolean isLogin;
public static LoginInterface loginSdk;
private Context mContext;
@Override
public void onCreate() {
super.onCreate();
loginSdk = new LoginSdk();
mContext = getApplicationContext();
}
class LoginSdk implements LoginInterface {
@Override
public void mLoginSuccess() {
Toast.makeText(mContext, "登陸成功", Toast.LENGTH_SHORT);
}
@Override
public void mLoginFail() {
Intent intent=new Intent(mContext, LoginActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
}
5.接口
package com.example.lcc.aoplogin;
public interface LoginInterface {
//登陸成功
public void mLoginSuccess();
//登陸失敗
public void mLoginFail();
}
6.mainActivity
package com.example.lcc.aoplogin;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import com.example.lcc.aoplogin.annotation.CheckLogin;
public class MainActivity extends AppCompatActivity {
private Button mButton1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton1 = findViewById(R.id.button_skip_one);
MyApplication.isLogin = false;
mButton1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
test();
}
});
}
@CheckLogin
public void test() {
startActivity(new Intent(MainActivity.this, TwoActivity.class));
}
}
五藤巢、其他
1.JoinPoint的作用
這個參數(shù)包含了切點的所有信息
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
String name = signature.getName(); // 方法名:test
Method method = signature.getMethod(); // 方法:public void com.example.lcc.aoplogin.MainActivity.test(android.view.View)
Class returnType = signature.getReturnType(); // 返回值類型:void
Class declaringType = signature.getDeclaringType(); // 方法所在類名:MainActivity
String[] parameterNames = signature.getParameterNames(); // 參數(shù)名:view
Class[] parameterTypes = signature.getParameterTypes(); // 參數(shù)類型:View
CheckLogin checkLogin = signature.getMethod().getAnnotation(CheckLogin.class);// 通過Method對象得到切點上的注解
六搞莺、參考資料
Android面向切面編程(AOP)
【翻譯】Android中的AOP編程
Android AOP面向切面編程詳解
git源碼
~~喵印