引言
之前有個做
Java Web
的師兄就跟我提過,我一直以為這是Java Web
的特產,也就是一個叫做Spring框架
的東西的核心思想。可能你屌,你學過Java Web
。。。反正我是沒有學過,所以我就沒怎么去撩這個概念。但是最近在學習響應式設計
的時候就接觸到了面向切面編程
這個概念,然后就去找資料咯。AOP
絕不是那么幾句話能解釋的,在寫這篇博文的時候,我也只是學了點皮毛。只是做了個小小的了解,有什么錯誤勿噴。哈哈。。。
什么是AOP(面向切面編程)
比較正式的定義
在軟件業,
AOP
為Aspect Oriented Programming
的縮寫,意為:面向切面編程
,通過預編譯方式
和運行期動態代理
實現程序功能的統一維護的一種技術。AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架
中的一個重要內容,是函數式編程的一種衍生范型。利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。
我的的一些理解(嘿嘿嘿)
其實從名字來看,根本就不知道這特么是個什么鳥東西。。。傳統的
OOP
思想是針對類和對象
設計的,Java里面一切皆對象
嘛。但是AOP
跟OOP
的思想好像是八竿子拉不上關系那種。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");
}
}
其實我也是走馬觀花地看一下,博文有什么不好的地方請指正。