Android中的AOP編程(AspectJ和Eclipse的那些事)

引言

之前有個做Java Web的師兄就跟我提過,我一直以為這是Java Web的特產,也就是一個叫做Spring框架的東西的核心思想。可能你屌,你學過Java Web。。。反正我是沒有學過,所以我就沒怎么去撩這個概念。但是最近在學習響應式設計的時候就接觸到了面向切面編程這個概念,然后就去找資料咯。AOP絕不是那么幾句話能解釋的,在寫這篇博文的時候,我也只是學了點皮毛。只是做了個小小的了解,有什么錯誤勿噴。哈哈。。。

什么是AOP(面向切面編程)

比較正式的定義

在軟件業,AOPAspect Oriented Programming的縮寫,意為:面向切面編程,通過預編譯方式運行期動態代理實現程序功能的統一維護的一種技術。AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生范型。利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。

我的的一些理解(嘿嘿嘿)

其實從名字來看,根本就不知道這特么是個什么鳥東西。。。傳統的OOP思想是針對類和對象設計的,Java里面一切皆對象嘛。但是AOPOOP的思想好像是八竿子拉不上關系那種。AOP主要就是把一些不屬于業務邏輯的代碼分離出去。例如我們經常調試用的日志記錄異常處理等。就是某些功能是大家都需要有的,而且代碼幾乎完全一樣。如果我們給每個功能都添加這些代碼,當這些所必須的功能代碼需要改變的時候。。。呵呵噠,你要全改!!!AOP就是結合預編譯運行期動態代理去給每個函數或者代碼去添加這些東西。其實這個概念有點像代理模式,就是說抽象的業務邏輯是不具體的,通過把業務邏輯抽象出來,然后添加自己的一些額外操作,而這些額外操作確實幾乎一樣的。不過AOP業務邏輯不是使用抽象抽取的方式。

AspectJ

啥玩意???

百度的說

AspectJ是一個面向切面的框架,它擴展了Java語言。AspectJ定義了AOP語法所以它有一個專門的編譯器用來生成遵守Java字節編碼規范的Class文件。

我的理解

就是一個面向切面的框架。。。遵循Java語言規范,并且有自己一套語法的框架。嗯。。。

怎么用

安裝AJDT

要使用這套框架,必須安裝AJDT,看名字是不是跟ADT差不多,是不是覺得他們分量是差不多的???其實AJDT也是基于ADT的,只不過是多了自己那么一套東西而已

下載AJDT插件

這里有一個AJDT的傳送門AJDT下載
大家一定要先看看自己Eclipse的版本!!!!
至于怎么看???不講!!!
因為國內下載速度慢得吃翔,所以就說一下離線安裝的步驟

  • 這里我的版本是4.5,所以我就找到Development builds for Eclipse 4.5。找到對應的See below點進去。。。然后去下載。PS:下載得慢的同學可以把地址扔進去百度云的離線下載

    下載頁面

  • 找到Eclipse的菜單項Help->Install New Software->add->Archive選中你剛剛下載的東西->OK->然后發現多了三個東西可以選select all->next。。。一直下一步下一步下一步還有各種安裝協議,統統給我同意。

  • 如果遇到問題,你就要看一下你下載的AJDT版本跟你的Eclipse版本是否一直

  • 安裝完了之后就可以直接重啟Eclipse了

添加所需要的Jar包到項目

再給一個傳送門AspectJ的Jar包

  • 我們解壓這個Jar包,然后提取aspectjrt.jar添加到我們的項目中并且添加為依賴包
    添加

到此為止,我們的環境就配置好了

實例

這里我們實現一個功能,就是去記錄用戶的軟件使用行為,其實很多APP都有做這方面的數據分析。統計用戶行為發送到云端,利用大數據分析用戶的喜好和行為習慣,從而打造出更加符合大眾的產品。

Android布局文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="tMall"
        android:text="天貓" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="bargain"
        android:text="聚劃算" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="market"
        android:text="超市" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="charge"
        android:text="充值" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="travel"
        android:text="阿里旅行" />

</LinearLayout>

傳統的做法

public class MainActivity extends Activity {
    
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    
    public void tMall(View v) {
        Log.i("Statistic", "天貓模塊被使用: " + sdf.format(new Date()));
        long begin = System.currentTimeMillis();
        try {
            Thread.currentThread().sleep(1000);
            Log.i("AOP", "哇,好多商品啊");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        Log.i("Statistic", "天貓模塊使用時長:" + (end - begin) + "ms");
    }
    
    public void bargain(View v) {
        Log.i("Statistic", "聚劃算模塊被使用: " + sdf.format(new Date()));
        long begin = System.currentTimeMillis();
        try {
            Thread.currentThread().sleep(1000);
            Log.i("AOP", "很多特價咧");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        Log.i("Statistic", "聚劃算模塊使用時長:" + (end - begin) + "ms");
    }
    
    public void travel(View v) {
        Log.i("Statistic", "阿里旅行模塊被使用: " + sdf.format(new Date()));
        long begin = System.currentTimeMillis();
        try {
            Thread.currentThread().sleep(1000);
            Log.i("AOP", "說走就走的旅行");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        Log.i("Statistic", "阿里旅行模塊使用時長:" + (end - begin) + "ms");
    }
    
    public void charge(View v) {
        Log.i("Statistic", "充值模塊被使用: " + sdf.format(new Date()));
        long begin = System.currentTimeMillis();
        try {
            Thread.currentThread().sleep(1000);
            Log.i("AOP", "給我沖個話費,叫你爸爸");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        Log.i("Statistic", "充值模塊使用時長:" + (end - begin) + "ms");
    }
    
    public void market(View v) {
        
        Log.i("Statistic", "天貓超市模塊被使用: " + sdf.format(new Date()));
        long begin = System.currentTimeMillis();
        try {
            Thread.currentThread().sleep(1000);
            Log.i("AOP", "買點庫存");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        Log.i("Statistic", "天貓超市模塊使用時長:" + (end - begin) + "ms");
    }
    
}

上面的代碼有一個就是用戶行為日志記錄,什么時候使用了什么功能。在該功能上面花費的時間是多少。這里的日志記錄的行為幾乎是一樣的,而且與具體的業務邏輯是沒有關系的。最要要命的是,如果需求需要改,不需要記錄具體年月日,主要時分秒、又或是需要把用戶所在的地區也要記錄下來。這時候可謂是牽一發而動全身。。。這時候我們就可以把這個小小的功能作為切入點,使用面向切面的思想去解決。

使用AspectJ框架解決

BehaviorTrack.java

package top.august1996.aopdemo.behaviorstatistic;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BehaviorTrack {
    String value();
}

上面我們使用注解的方式去定位我們需要添加日志記錄功能的方法,如果對注解不太了解的話可以參考如何做一個簡單的注解框架

MainActivity2.java

public class MainActivity2 extends Activity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    
    @BehaviorTrack("天貓模塊")
    public void tMall(View v) {
        try {
            Thread.currentThread().sleep(1000);
            Log.i("AOP", "哇,好多商品啊");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    @BehaviorTrack("聚劃算模塊")
    public void bargain(View v) {
        try {
            Thread.currentThread().sleep(1000);
            Log.i("AOP", "很多特價咧");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    @BehaviorTrack("阿里旅行模塊")
    public void travel(View v) {
        try {
            Thread.currentThread().sleep(1000);
            Log.i("AOP", "說走就走的旅行");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    @BehaviorTrack("充值模塊")
    public void charge(View v) {
        try {
            Thread.currentThread().sleep(1000);
            Log.i("AOP", "給我沖個話費,叫你爸爸");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    @BehaviorTrack("天貓超市模塊")
    public void market(View v) {
        try {
            Thread.currentThread().sleep(1000);
            Log.i("AOP", "買點庫存");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
}

上面,我們只是添加了一個@BehaviorTrack的注解,并且聲明了相關信息

BehaviorAspect.java

這里就是我們的重頭戲了

@Aspect
public class BehaviorAspect {
    
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
    @Around("execution(@top.august1996.aopdemo.behaviorstatistic.BehaviorTrack * *(..))")
    /*
     * 1、top.august1996.aopdemo.behaviorstatistic.BehaviorTrack是你定義注解的包名+類名
     * 2、第一個*:規則應用于所有包里面的類。如果指定類A,那么AJDT就會只去找類A里面的注解
     * 3、第二個*:規則應用于類里面的所有方法。如果指定方法A,那么AJDT就會只去找叫做方法A的方法應用規則
     */
    
    public void waveJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
        
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        BehaviorTrack behaviorTrack = method.getAnnotation(BehaviorTrack.class);
        
        Log.i("Statistic", behaviorTrack.value() + "被使用: " + sdf.format(new Date()));
        long begin = System.currentTimeMillis();
        
        joinPoint.proceed();
        // 類似于代理,就是具體的邏輯業務。這里是某個方法
        
        long end = System.currentTimeMillis();
        Log.i("Statistic", behaviorTrack.value() + (end - begin) + "ms");
    }
}

其實我也是走馬觀花地看一下,博文有什么不好的地方請指正。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容