AOP註解日誌處理簡單理解
理解註解:
1、首先註解是在JDK5.0及以後版本中引入的。他可以用於建立文件,跟蹤程式碼中的依賴性,甚至執行基本的編譯時檢查,而且還可以作為在spring框架中進行註解試注入Bean屬性,用來減少寫配置檔案。註解是以@+註解名在程式碼中存在的,根據註解引數的個數,我們可以將註解分為:標記註解,單值註解、完整註解三類。他們都不會直接影響到程式的語義,只是作為註解存在,可以通過反射機制程式設計實現對這些元資料的訪問。另外,你可以在編譯時選擇程式碼哩的註釋是否只存在於原始碼級,或者它也能在class檔案中出現。
在這裡所說的元資料作用可以大致分為三類:生成文件,編譯時檢查,跟蹤程式碼的依賴性,代替已有的配置檔案。
2、註釋的3中基本型別
a、標記註釋 --沒有變數,只有名稱標示 例如:@annotation
b、單一值註釋 --在標記註釋的基礎上提供一段資料。 例如:@annotation("data")
c、完整註釋 --可以包括多個數據成員,每個資料成員由名稱和值構成
例如:@annotation(val1="data1",val2="data2")
3、java中提供了3個內建註釋型別
a、override,這個大家都不陌生,他是隻能用在方法上,對方法的重寫
作用:可以保證編譯時Override函式的宣告正確性
用法:@override
b、Deprecated 同樣只能作用於方法
作用:對不應在使用的方法進行註解
用法:@Deprecated 並且他必須得和這個函式放在同一行,現在不在經常使用
c、SuperssWarnings 可以註釋一段程式碼
作用:關閉特定的告警資訊,
用法:@SuperssWarnings (value={"unchecked"})程式碼
這三個只是簡單的註解型別
4、在java中最重要的是元註解,元註解是什麼呢?
元註解是用來描述註解的註解,就是用來註解其他的註解,為什麼會出現它呢,因為我們在專案中要用到自定義的註解,所以這些自定義的註解就是要用這些元註解來註解的。
要寫一個自定義的註解,必須通過@interface關鍵字來定義,在你這個類之前,就需要通過這些個元註解來描述該註解的使用範圍(@Target
a、@Target 表示該註釋可以用於什麼地方,可用的引數ElementType引數包括:
CONSTRUCTOR:說明此註解只能用於構造器
FIELD:說明只能用於域的宣告
LOCAL_VARIABLE:區域性變數宣告
METHOD:方法宣告
PACKAGE:包宣告
PARAMETER:引數宣告
TYPE:類、介面(包括註解型別)或者是enum宣告
b、@Retention 表示需要在什麼級別儲存該註釋資訊。可選的RetentionPolicy引數包括
SOURCE:註釋將被編譯器丟掉
CLASS:註釋在class檔案中可用,但會被vm丟棄
RUNTIME:VM將在執行時也保障註釋,因此可以通過反射機制讀取註釋的資訊。
c、@Documented 將註釋包含在JavaDoc中
d、@Inheried 允許子類繼承父類中的註釋。
5、寫個簡單的自定義註解例子:
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationName {
String value();
}
//這個註解只能用在類、介面或者是enum、方法名、之前,並且在執行時保持註解
使用這個註解:
@AnnotationName(value="test")
public class Test {
public Test(){
}
}
6、如果你要記錄日誌,那麼這個註解中的值你可以寫成你此刻執行的方法,在這裡就有問題出現了,你註解到這裡你怎麼去讀取你註解裡的value值呢,
這裡就用到了註解處理器,註解處理器其實就是一段處理自己編寫的註解類的程式碼而已。並不用太多複雜的概念或者技術可以使用反射的方法去讀取註解裡邊的值:在這裡就必須將RetentionPolicy指定為RUNTIME
例子:
A、這是你執行的那個註解處理器
public class TestExcute {
public void getLog(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("****************************註解直譯器執行開始*********************************");
//獲取目標物件對應的類名
System.out.println("應用註解的類名:"+joinPoint.getTarget().getClass());
//獲取實現類繼承的介面名
@SuppressWarnings("rawtypes")
Class[] c = joinPoint.getTarget().getClass().getInterfaces();
System.out.println("註解物件所實現的介面名:"+c[0]);
//獲取到了註解在這個service實現類上的annotation
Annotation[] a = joinPoint.getTarget().getClass().getAnnotations();
//獲取這個類上的註解的個數
System.out.println("應用註解類上的註解個數:"+a.length);
//判斷這個類上面的註釋是否是AnnotationName這個自定義的註解,如果是返回這個註解,如果不是返回null
if(joinPoint.getTarget().getClass().getAnnotation(AnnotationName.class)!=null){
//獲取到這個類上的註解
AnnotationName anns = joinPoint.getTarget().getClass().getAnnotation(AnnotationName.class);
//輸出這個類上的註解的值
System.out.println("註釋在實現類上的annotation:"+anns.value());
}
//判斷這個介面上是否存在此註解
if(c[0].getAnnotation(AnnotationName.class)!=null){
AnnotationName an = (AnnotationName) c[0].getAnnotation(AnnotationName.class);
System.out.println("註解物件所實現介面上的註解值:"+an.value());
}
//獲取目標物件上正在執行的方法名
String methodString = joinPoint.getSignature().getName();
System.out.println("目標物件上正在執行的方法名:"+methodString);
//獲取到這個類上面的方法全名
Method meths[] = joinPoint.getSignature().getDeclaringType().getMethods();
System.out.println("方法上面的全名:"+meths[0]);
//獲取到這個類上面的方法上面的註釋
Annotation[] anns = meths[0].getDeclaredAnnotations();
System.out.println("正在執行方法上面的註釋:"+((AnnotationName)anns[0]).value());
//讓你註釋的那個方法執行
joinPoint.proceed();
//完畢
System.out.println("****************************註解直譯器執行完畢*********************************");
}
}
B、這個註解直譯器所使用的是jdk自己的代理類生成機制,所以你所要記錄的類必須是繼承自一個介面,因為他這個生成的代理類可以理解為介面和你這個實現類之間的一個類,所以這個記錄的類必須是要實現一個介面
介面:
@AnnotationName(value="Test1測試interface")
public interface Test1 {
@AnnotationName(value="Test1測試testAspect")
public void testAspect();
}
實現類:
@AnnotationName(value="Test測試業務Test2")
public class Test2 implements Test1{
@Override
public void testAspect() {
// TODO Auto-generated method stub
System.out.println("Test2 is excuting。。。。。。。。");
}
}
C、Action:
public class Test extends ActionSupport{
private static final long serialVersionUID = 1L;
private Test1 test2;
public Test1 getTest2() {
return test2;
}
public void setTest2(Test1 test2) {
this.test2 = test2;
}
public void getTest(){
test2.testAspect();
System.out.println("=========================執行Action======================");
}
}
D、在spring配置檔案中的配置:
<!-- 開啟AOP的攔截功能,proxy-target-class:是用來控制注入的時實現類還是介面,預設是false表示只能注入介面,true:表示可以用類來注入-->
<aop:aspectj-autoproxy proxy-target-class="false"/>
<aop:config>
<!-- 宣告一個切面 -->
<aop:aspect id="testExcuteAspect" ref="testExcute">
<!-- 宣告一個裝備,是在什麼時候執行日誌處理 -->
<!-- 宣告一個切入點,
expression: 第一個*表示:萬用字元,表示返回任何型別,
第二個*表示:萬用字元,表示這個包下的任意類
第三個*表示:萬用字元,表示這個類下的方法可以有0個或多個引數
(..):表示這個包下的所有子包
-->
<aop:pointcut expression="execution(* com.ckpt.logPackage.*.*(..))" id="logTest" />
<aop:around method="getLog" pointcut-ref="logTest" />
</aop:aspect>
</aop:config>
<bean id="testExcute" class="com.ckpt.logPackage.TestExcute" scope="prototype"></bean>
<bean id="test" class="com.ckpt.Action.Test" scope="prototype">
<property name="test2" ref="test2"></property>
</bean>
<bean id="test2" class="com.ckpt.logPackage.Test2" scope="prototype"></bean>
E、struts2配置檔案:
<action name="test" class="test" method="getTest" >
<result type="plainText"></result>
</action>
F、釋出專案,在瀏覽器中直接訪問Action打印出日誌
7、ProceedingJoinPoint :使用這個類取處理日誌,只能處理環繞通知
ProceedingJoinPoint is only supported for around advice