AOP概念:
把我們某個方面的功能提出來與一批對象進行隔離,這樣與一批對象之間的耦合度就降低了,就只需要對某個功能進行編程。例如android中的登陸權限問題,只需要在特定的方法加入我們的登陸切點,在不改變業務邏輯的情況下可以變更我們判斷登錄 的業務邏輯,這樣就達到了鬆耦合的目的。在編譯成.class時注入
基礎知識
AspectJ
是一個面向切面編程的框架,他擴展了java語言所以他有一個專門的編譯器用來生成遵循java字節碼規範的Class文件
aspect:切面 [整個程序塊]
Pointcut:切點 [進行切分的線]
Around:處理
註解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
定義一個註解都要包含 @Target @Retention
@Target 標識運用在哪裏(是方法/類/....)
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
}
@Retention 運行在哪個地方 Source 註解僅存在於源碼中,在class字節碼文件中不包含
class 默認的保留策略,註解會在class字節碼文件中存在,但運行時無法獲得
runtime 註解會在class字節碼文件中存在,在運行時可以通過反射獲取到
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
案例
下面將以一個簡單的案例來分析登錄的註解情況。
實現VIP登錄校驗,並彈出toast
gradle引入AspectJ
app.gradle中
apply plugin: 'com.android.application'
apply plugin: 'android-aspectjx'
compile 'org.aspectj:aspectjrt:1.8.13'
project.gradle中
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.0'
}
定義註解:
/**
* @author crazyZhangxl on 2019/1/28.
* Describe: 需要登陸驗證的切點 注意在interface前加入@
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginTrace {
}
定義切點切面以及特定的處理:
/**
* @author crazyZhangxl on 2019/1/28.
* Describe: 切面
*/
@Aspect
public class LoginAspect {
/**
* 對含有某個方法的特定註解打上切點
*/
@Pointcut("execution(@com.example.aopproject.login_demo.LoginTrace * *(..))")
public void pointCutLogin(){
}
/**
* 處理 特定的打上切點的方法
* @param proceedingJoinPoint
* @throws Throwable
*/
@Around("pointCutLogin()")
public void aroundLogin(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
if (UserCache.getInstance().isLogin()){
proceedingJoinPoint.proceed();
}else {
Toast.makeText(MyApp.getmContext(), "請先進行登陸!", Toast.LENGTH_SHORT).show();
}
}
}
方法業務邏輯
@LoginTrace()
private void methodTwo(){
Toast.makeText(this, "執行VIP動作", Toast.LENGTH_SHORT).show();
}
具體的運行效果:
在登錄的情況下進行VIP瀏覽操作成功,而若在未登錄的情況下操作時那麼會進行彈框提示。
優化:
上一個案例只是最簡單的加了aop方法運行時的處理,下面將進行拓展,讓外部去實現具體需要在未登錄的情況下去執行哪些處理
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginTrace {
int type();
}
/**
* 處理 特定的打上切點的方法
* @param proceedingJoinPoint
* @throws Throwable
*/
@Around("pointCutLogin()")
public void aroundLogin(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
if (UserCache.getInstance().isLogin()){
proceedingJoinPoint.proceed();
}else {
if (proceedingJoinPoint.getThis() instanceof Context){
// 獲得註解參數
MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
LoginTrace annotation = signature.getMethod().getAnnotation(LoginTrace.class);
int type = annotation.type();
dealWithType(type,(Context) proceedingJoinPoint.getThis());
}
}
}
/**
* 在這裏處理是彈出dialog呢還是跳轉界面呢 等等
* @param type
* @param context
*/
private void dealWithType(int type,Context context){
switch (type){
case 0:
Intent intent = new Intent(context,LoginActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
break;
default:
Toast.makeText( context,"請先進行登陸!", Toast.LENGTH_SHORT).show();
break;
}
}
參考資料: