一. AOP 面向切面架構(gòu)設(shè)計 - 動態(tài)代理切面需求
需求:在數(shù)據(jù)操作前進(jìn)行備份操作,進(jìn)行橫向切片
DBOperation.kt
interface DBOperation {
fun insert()
fun delete()
fun update()
fun save()
}
DBActivity.kt
/**
* 每次操作前都要進(jìn)行一次save
*
* 運(yùn)行時動態(tài)代理做切面,每次操作前都先save
*/
class DBActivity : AppCompatActivity(), DBOperation {
private lateinit var db: DBOperation
private val TAG = "DBActivity";
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_d_b)
db = Proxy.newProxyInstance(
DBOperation::class.java.classLoader,
arrayOf(DBOperation::class.java),
DBHandler(this)
) as DBOperation
initListener()
}
private fun initListener() {
mBtn.setOnClickListener {
db.update()
}
}
inner class DBHandler(private val dbOperation: DBOperation) : InvocationHandler {
@Throws
override fun invoke(proxy: Any, method: Method, args: Array<out Any>): Any? {
Log.e(TAG, "操作數(shù)據(jù)庫之前開始備份...")
//查詢數(shù)據(jù)后備份咆繁,詳細(xì)操作過程省略
save()
Log.e(TAG, "數(shù)據(jù)備份完成攻泼,等待操作...")
return method.invoke(dbOperation, *args)
}
}
override fun insert() {
Log.e(TAG, "數(shù)據(jù)插入中...")
}
override fun delete() {
Log.e(TAG, "數(shù)據(jù)刪除中...")
}
override fun update() {
Log.e(TAG, "數(shù)據(jù)更新中...")
}
override fun save() {
Log.e(TAG, "數(shù)據(jù)備份中...")
}
}
注 :在使用Kotlin時的可變參數(shù)
return method.invoke(dbOperation, *args)
有個*
二. 面向切面思想之集中式登錄架構(gòu)設(shè)計
WX20200826-151427@2x.png
普通 java ->class javac
AspectJ會有一套符合java字節(jié)碼編碼規(guī)范的編譯工具來替代javac,在將.java文件編譯為.class文件時孔飒,會動態(tài)的插入一些代碼來做到對某一類特定東西的統(tǒng)一處理
根gradle
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.0'
// 版本界限:As-3.0.1 + gradle4.4-all (需要配置r17的NDK環(huán)境)
// 或者:As-3.2.1 + gradle4.6-all (正常使用选调,無警告)
classpath 'org.aspectj:aspectjtools:1.8.9'
classpath 'org.aspectj:aspectjweaver:1.8.9'
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
app gradle
apply plugin: 'com.android.application'
// 版本界限:As-3.0.1 + gradle4.4-all (需要配置r17的NDK環(huán)境)
// 或者:As-3.2.1 + gradle4.6-all (正常使用顷编,無警告)
buildscript { // 編譯時用Aspect專門的編譯器暖释,不再使用傳統(tǒng)的javac
repositories {
mavenCentral()
}
dependencies {
classpath 'org.aspectj:aspectjtools:1.8.9'
classpath 'org.aspectj:aspectjweaver:1.8.9'
}
}
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.netease.aop.login"
minSdkVersion 19
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
testImplementation 'junit:junit:4.12'
implementation 'org.aspectj:aspectjrt:1.8.13'
}
// 版本界限:As-3.0.1 + gradle4.4-all (需要配置r17的NDK環(huán)境)
// 或者:As-3.2.1 + gradle4.6-all (正常使用袭厂,無警告)
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
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;
}
}
}
}
ClickBehavior.java
// 用戶點(diǎn)擊痕跡(行為統(tǒng)計) IoC容器
@Target(ElementType.METHOD) // 目標(biāo)作用在方法之上
@Retention(RetentionPolicy.RUNTIME)
public @interface ClickBehavior {
String value();
}
LoginCheck.java
// 用戶登錄檢測
@Target(ElementType.METHOD) // 目標(biāo)作用在方法之上
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginCheck {
}
LoginCheckAspect.java
package com.netease.aop.login.aspect;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;
import com.netease.aop.login.LoginActivity;
import com.netease.aop.login.annotation.ClickBehavior;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
@Aspect // 定義切面類
public class LoginCheckAspect {
private final static String TAG = "netease >>> ";
// 1、應(yīng)用中用到了哪些注解球匕,放到當(dāng)前的切入點(diǎn)進(jìn)行處理(找到需要處理的切入點(diǎn))
// execution纹磺,以方法執(zhí)行時作為切點(diǎn),觸發(fā)Aspect類
// * *(..)) 可以處理ClickBehavior這個類所有的方法
@Pointcut("execution(@com.netease.aop.login.annotation.LoginCheck * *(..))")
public void methodPointCut() {}
// 2亮曹、對切入點(diǎn)如何處理
@Around("methodPointCut()")
public Object jointPotin(ProceedingJoinPoint joinPoint) throws Throwable {
Context context = (Context) joinPoint.getThis();
if (true) { // 從SharedPreferences中讀取
Log.e(TAG, "檢測到已登錄橄杨!");
return joinPoint.proceed();
} else {
Log.e(TAG, "檢測到未登錄秘症!");
Toast.makeText(context, "請先登錄!", Toast.LENGTH_SHORT).show();
context.startActivity(new Intent(context, LoginActivity.class));
return null; // 不再執(zhí)行方法(切入點(diǎn))
}
}
}
ClickBehaviorAspect.java
package com.netease.aop.login.aspect;
import android.util.Log;
import com.netease.aop.login.annotation.ClickBehavior;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
@Aspect // 定義切面類
public class ClickBehaviorAspect {
private final static String TAG = "netease >>> ";
// 1式矫、應(yīng)用中用到了哪些注解乡摹,放到當(dāng)前的切入點(diǎn)進(jìn)行處理(找到需要處理的切入點(diǎn))
// execution,以方法執(zhí)行時作為切點(diǎn)采转,觸發(fā)Aspect類
// * *(..)) 可以處理ClickBehavior這個類所有的方法
@Pointcut("execution(@com.netease.aop.login.annotation.ClickBehavior * *(..))")
public void methodPointCut() {}
// 2趟卸、對切入點(diǎn)如何處理
@Around("methodPointCut()")
public Object jointPotin(ProceedingJoinPoint joinPoint) throws Throwable {
// 獲取簽名方法
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
// 獲取方法所屬的類名
String className = methodSignature.getDeclaringType().getSimpleName();
// 獲取方法名
String methodName = methodSignature.getName();
// 獲取方法的注解值(需要統(tǒng)計的用戶行為)
String funName = methodSignature.getMethod().getAnnotation(ClickBehavior.class).value();
// 統(tǒng)計方法的執(zhí)行時間、統(tǒng)計用戶點(diǎn)擊某功能行為氏义。(存儲到本地,每過x天上傳到服務(wù)器)
long begin = System.currentTimeMillis();
Log.e(TAG, "ClickBehavior Method Start >>> ");
Object result = joinPoint.proceed(); // MainActivity中切面的方法
long duration = System.currentTimeMillis() - begin;
Log.e(TAG, "ClickBehavior Method End >>> ");
Log.e(TAG, String.format("統(tǒng)計了:%s功能图云,在%s類的%s方法惯悠,用時%d ms",
funName, className, methodName, duration));
return result;
}
}
MainActivity.java
package com.netease.aop.login;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import com.netease.aop.login.annotation.ClickBehavior;
import com.netease.aop.login.annotation.LoginCheck;
public class MainActivity extends AppCompatActivity {
private final static String TAG = "netease >>> ";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
// 登錄點(diǎn)擊事件(用戶行為統(tǒng)計)
@ClickBehavior("登錄")
public void login(View view) {
Log.e(TAG, "模擬接口請求……驗證通過,登錄成功竣况!");
}
// 用戶行為統(tǒng)計(友盟統(tǒng)計克婶?!后臺要求自己統(tǒng)計)
@ClickBehavior("我的專區(qū)")
@LoginCheck
public void area(View view) {
Log.e(TAG, "開始跳轉(zhuǎn)到 -> 我的專區(qū) Activity");
startActivity(new Intent(this, OtherActivity.class));
}
// 用戶行為統(tǒng)計
@ClickBehavior("我的優(yōu)惠券")
@LoginCheck
public void coupon(View view) {
Log.e(TAG, "開始跳轉(zhuǎn)到 -> 我的優(yōu)惠券 Activity");
startActivity(new Intent(this, OtherActivity.class));
}
// 用戶行為統(tǒng)計
@ClickBehavior("我的積分")
@LoginCheck
public void score(View view) {
Log.e(TAG, "開始跳轉(zhuǎn)到 -> 我的積分 Activity");
startActivity(new Intent(this, OtherActivity.class));
}
}