【Java注解系列】内置注解与AOP实现自定义注解
Java 注解(Annotation)又稱 Java 標注,是 JDK5.0 引入的一種注釋機制。
Java 語言中的類、方法、變量、參數和包等都可以被標注。和 Javadoc 不同,Java 標注可以通過反射獲取標注內容。在編譯器生成類文件時,標注可以被嵌入到字節碼中。Java 虛擬機可以保留標注內容,在運行時可以獲取到標注內容 。 當然它也支持自定義 Java 標注。
內置的注解
Java 定義了一套注解,共有 7 個,3 個在 java.lang 中,剩下 4 個在 java.lang.annotation 中。
作用在代碼的注解是
- @Override - 檢查該方法是否是重寫方法。如果發現其父類,或者是引用的接口中并沒有該方法時,會報編譯錯誤。
- @Deprecated - 標記過時方法。如果使用該方法,會報編譯警告。
- @SuppressWarnings - 指示編譯器去忽略注解中聲明的警告。
作用在其他注解的注解(或者說 元注解)是:
- @Retention - 標識這個注解怎么保存,是只在代碼中,還是編入class文件中,或者是在運行時可以通過反射訪問。
- @Documented - 標記這些注解是否包含在用戶文檔中。
- @Target - 標記這個注解應該是哪種 Java 成員。
- @Inherited - 標記這個注解是繼承于哪個注解類(默認 注解并沒有繼承于任何子類)
從 Java 7 開始,額外添加了 3 個注解:
- @SafeVarargs - Java 7 開始支持,忽略任何使用參數為泛型變量的方法或構造函數調用產生的警告。
- @FunctionalInterface - Java 8 開始支持,標識一個匿名函數或函數式接口。
- @Repeatable - Java 8 開始支持,標識某注解可以在同一個聲明上使用多次。
(01) Annotation 就是個接口。
"每 1 個 Annotation" 都與 "1 個 RetentionPolicy" 關聯,并且與 "1~n 個 ElementType" 關聯。可以通俗的理解為:每 1 個 Annotation 對象,都會有唯一的 RetentionPolicy 屬性;至于 ElementType 屬性,則有 1~n 個。
(02) ElementType 是 Enum 枚舉類型,它用來指定 Annotation 的類型。
"每 1 個 Annotation" 都與 "1~n 個 ElementType" 關聯。當 Annotation 與某個 ElementType 關聯時,就意味著:Annotation有了某種用途。例如,若一個 Annotation 對象是 METHOD 類型,則該 Annotation 只能用來修飾方法。
(03) RetentionPolicy 是 Enum 枚舉類型,它用來指定 Annotation 的策略。通俗點說,就是不同 RetentionPolicy 類型的 Annotation 的作用域不同。
"每 1 個 Annotation" 都與 "1 個 RetentionPolicy" 關聯。
- a) 若 Annotation 的類型為 SOURCE,則意味著:Annotation 僅存在于編譯器處理期間,編譯器處理完之后,該 Annotation 就沒用了。 例如," @Override" 標志就是一個 Annotation。當它修飾一個方法的時候,就意味著該方法覆蓋父類的方法;并且在編譯期間會進行語法檢查!編譯器處理完后,"@Override" 就沒有任何作用了。
- b) 若 Annotation 的類型為 CLASS,則意味著:編譯器將 Annotation 存儲于類對應的 .class 文件中,它是 Annotation 的默認行為。
- c) 若 Annotation 的類型為 RUNTIME,則意味著:編譯器將 Annotation 存儲于 class 文件中,并且可由JVM讀入。
這時,只需要記住"每 1 個 Annotation" 都與 "1 個 RetentionPolicy" 關聯,并且與 "1~n 個 ElementType" 關聯。學完后面的內容之后,再回頭看這些內容,會更容易理解。
java 自帶的 Annotation
理解了上面的 3 個類的作用之后,我們接下來可以講解 Annotation 實現類的語法定義了。
@Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation1 { }說明:
上面的作用是定義一個 Annotation,它的名字是 MyAnnotation1。定義了 MyAnnotation1 之后,我們可以在代碼中通過 "@MyAnnotation1" 來使用它。 其它的,@Documented, @Target, @Retention, @interface 都是來修飾 MyAnnotation1 的。下面分別說說它們的含義:
(01) @interface
使用 @interface 定義注解時,意味著它實現了 java.lang.annotation.Annotation 接口,即該注解就是一個Annotation。
定義 Annotation 時,@interface 是必須的。
注意:它和我們通常的 implemented 實現接口的方法不同。Annotation 接口的實現細節都由編譯器完成。通過 @interface 定義注解后,該注解不能繼承其他的注解或接口。
(02) @Documented?
類和方法的 Annotation 在缺省情況下是不出現在 javadoc 中的。如果使用 @Documented 修飾該 Annotation,則表示它可以出現在 javadoc 中。
定義 Annotation 時,@Documented 可有可無;若沒有定義,則 Annotation 不會出現在 javadoc 中。
(03) @Target(ElementType.TYPE)
前面我們說過,ElementType 是 Annotation 的類型屬性。而 @Target 的作用,就是來指定 Annotation 的類型屬性。
@Target(ElementType.TYPE) 的意思就是指定該 Annotation 的類型是 ElementType.TYPE。這就意味著,MyAnnotation1 是來修飾"類、接口(包括注釋類型)或枚舉聲明"的注解。
定義 Annotation 時,@Target 可有可無。若有 @Target,則該 Annotation 只能用于它所指定的地方;若沒有 @Target,則該 Annotation 可以用于任何地方。
(04) @Retention(RetentionPolicy.RUNTIME)
前面我們說過,RetentionPolicy 是 Annotation 的策略屬性,而 @Retention 的作用,就是指定 Annotation 的策略屬性。
@Retention(RetentionPolicy.RUNTIME) 的意思就是指定該 Annotation 的策略是 RetentionPolicy.RUNTIME。這就意味著,編譯器會將該 Annotation 信息保留在 .class 文件中,并且能被虛擬機讀取。
定義 Annotation 時,@Retention 可有可無。若沒有 @Retention,則默認是 RetentionPolicy.CLASS。
2)java自帶的Annotation
通過上面的示例,我們能理解:@interface 用來聲明 Annotation,@Documented 用來表示該 Annotation 是否會出現在 javadoc 中, @Target 用來指定 Annotation 的類型,@Retention 用來指定 Annotation 的策略。
理解這一點之后,我們就很容易理解 java 中自帶的 Annotation 的實現類,即 Annotation 架構圖的右半邊。如下圖:
java 常用的 Annotation:
@Deprecated -- @Deprecated 所標注內容,不再被建議使用。 @Override -- @Override 只能標注方法,表示該方法覆蓋父類中的方法。 @Documented -- @Documented 所標注內容,可以出現在javadoc中。 @Inherited -- @Inherited只能被用來標注“Annotation類型”,它所標注的Annotation具有繼承性。 @Retention -- @Retention只能被用來標注“Annotation類型”,而且它被用來指定Annotation的RetentionPolicy屬性。 @Target -- @Target只能被用來標注“Annotation類型”,而且它被用來指定Annotation的ElementType屬性。 @SuppressWarnings -- @SuppressWarnings 所標注內容產生的警告,編譯器會對這些警告保持靜默。Annotation 的作用
Annotation 是一個輔助類,它在 Junit、Struts、Spring 等工具框架中被廣泛使用。
我們在編程中經常會使用到的 Annotation 作用有:
1)編譯檢查
Annotation 具有"讓編譯器進行編譯檢查的作用"。
例如,@SuppressWarnings, @Deprecated 和 @Override 都具有編譯檢查作用。
(02) 若某個方法被 @Override 的標注,則意味著該方法會覆蓋父類中的同名方法。如果有方法被 @Override 標示,但父類中卻沒有"被 @Override 標注"的同名方法,則編譯器會報錯。示例如下:
在反射中使用 Annotation
在反射的 Class, Method, Field 等函數中,有許多于 Annotation 相關的接口。
這也意味著,我們可以在反射中解析并使用 Annotation。
?
import java.lang.annotation.Annotation; import java.lang.annotation.Target; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Inherited; import java.lang.reflect.Method;/*** Annotation在反射函數中的使用示例*/ @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation {String[] value() default "unknown"; }/*** Person類。它會使用MyAnnotation注解。*/ class Person {/*** empty()方法同時被 "@Deprecated" 和 "@MyAnnotation(value={"a","b"})"所標注 * (01) @Deprecated,意味著empty()方法,不再被建議使用* (02) @MyAnnotation, 意味著empty() 方法對應的MyAnnotation的value值是默認值"unknown"*/@MyAnnotation@Deprecatedpublic void empty(){System.out.println("\nempty");}/*** sombody() 被 @MyAnnotation(value={"girl","boy"}) 所標注,* @MyAnnotation(value={"girl","boy"}), 意味著MyAnnotation的value值是{"girl","boy"}*/@MyAnnotation(value={"girl","boy"})public void somebody(String name, int age){System.out.println("\nsomebody: "+name+", "+age);} }public class AnnotationTest {public static void main(String[] args) throws Exception {// 新建PersonPerson person = new Person();// 獲取Person的Class實例Class<Person> c = Person.class;// 獲取 somebody() 方法的Method實例Method mSomebody = c.getMethod("somebody", new Class[]{String.class, int.class});// 執行該方法mSomebody.invoke(person, new Object[]{"lily", 18});iteratorAnnotations(mSomebody);// 獲取 somebody() 方法的Method實例Method mEmpty = c.getMethod("empty", new Class[]{});// 執行該方法mEmpty.invoke(person, new Object[]{}); iteratorAnnotations(mEmpty);}public static void iteratorAnnotations(Method method) {// 判斷 somebody() 方法是否包含MyAnnotation注解if(method.isAnnotationPresent(MyAnnotation.class)){// 獲取該方法的MyAnnotation注解實例MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);// 獲取 myAnnotation的值,并打印出來String[] values = myAnnotation.value();for (String str:values)System.out.printf(str+", ");System.out.println();}// 獲取方法上的所有注解,并打印出來Annotation[] annotations = method.getAnnotations();for(Annotation annotation : annotations){System.out.println(annotation);}} }運行結果:
somebody: lily, 18 girl, boy, @com.skywang.annotation.MyAnnotation(value=[girl, boy])empty unknown, @com.skywang.annotation.MyAnnotation(value=[unknown]) @java.lang.Deprecated()根據 Annotation 生成幫助文檔
通過給 Annotation 注解加上 @Documented 標簽,能使該 Annotation 標簽出現在 javadoc 中。
4能夠幫忙查看查看代碼
通過 @Override, @Deprecated 等,我們能很方便的了解程序的大致結構。
另外,我們也可以通過自定義 Annotation 來實現一些功能。
java之aop使用及自定義注解
案例一(獲取類與方法上的注解值):
TranscationModel
package com.huangting.annotation.P1; /*** enum枚舉*/ public enum TranscationModel {Read, ReadWrite, Write }MyAnnotation1
package com.huangting.annotation.P1;import java.lang.annotation.*;/*** MyAnnotation1注解可以用在類、接口、屬性、方法上* 注解運行期也保留* 不可繼承*/ @Target({ElementType.TYPE, ElementType.FIELD,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MyAnnotation1 {String name(); }MyAnnotation2
package com.huangting.annotation.P1;import java.lang.annotation.*;/*** MyAnnotation2注解可以用在方法上* 注解運行期也保留* 不可繼承*/ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MyAnnotation2 {TranscationModel model() default TranscationModel.ReadWrite; }MyAnnotation3?
package com.huangting.annotation.P1;import java.lang.annotation.*;/*** MyAnnotation3注解可以用在方法上* 注解運行期也保留* 可繼承*/ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface MyAnnotation3 {TranscationModel[] models() default TranscationModel.ReadWrite; }Dome1
package com.huangting.annotation.P1;/*** 獲取類與方法上的注解值*/ @MyAnnotation1(name = "abc") public class Demo1 {@MyAnnotation1(name = "xyz")private Integer age;@MyAnnotation2(model = TranscationModel.Read)public void list() {System.out.println("list");}@MyAnnotation3(models = {TranscationModel.Read, TranscationModel.Write})public void edit() {System.out.println("edit");} }Demo1Test
package com.huangting.annotation.P1; import org.junit.Test; public class Demo1Test {@Testpublic void list() throws Exception { // 獲取類上的注解MyAnnotation1 annotation1 = Demo1.class.getAnnotation(MyAnnotation1.class);System.out.println(annotation1.name());//abc// 獲取方法上的注解MyAnnotation2 myAnnotation2 = Demo1.class.getMethod("list").getAnnotation(MyAnnotation2.class);System.out.println(myAnnotation2.model());//Read}@Testpublic void edit() throws Exception {MyAnnotation3 myAnnotation3 = Demo1.class.getMethod("edit").getAnnotation(MyAnnotation3.class);for (TranscationModel model : myAnnotation3.models()) {System.out.println(model);//Read,Write}} }案例二(獲取類屬性上的注解屬性值)
TestAnnotation
package com.huangting.annotation.P2;import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;//@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface TestAnnotation {String value() default "默認value值";String what() default "這里是默認的what屬性對應的值"; } package com.huangting.annotation.P2;/*** 獲取類屬性上的注解屬性值*/ public class Demo2 {@TestAnnotation(value = "這就是value對應的值_msg1", what = "這就是what對應的值_msg1")private static String msg1;@TestAnnotation("這就是value對應的值1")private static String msg2;@TestAnnotation(value = "這就是value對應的值2")private static String msg3;@TestAnnotation(what = "這就是what對應的值")private static String msg4; } Demo2Test package com.huangting.annotation.P2;import org.junit.Test;public class Demo2Test {@Testpublic void test1() throws Exception {TestAnnotation msg1 = Demo2.class.getDeclaredField("msg1").getAnnotation(TestAnnotation.class);System.out.println(msg1.value());System.out.println(msg1.what());}@Testpublic void test2() throws Exception{TestAnnotation msg2 = Demo2.class.getDeclaredField("msg2").getAnnotation(TestAnnotation.class);System.out.println(msg2.value());System.out.println(msg2.what());}@Testpublic void test3() throws Exception{TestAnnotation msg3 = Demo2.class.getDeclaredField("msg3").getAnnotation(TestAnnotation.class);System.out.println(msg3.value());System.out.println(msg3.what());}@Testpublic void test4() throws Exception{TestAnnotation msg4 = Demo2.class.getDeclaredField("msg4").getAnnotation(TestAnnotation.class);System.out.println(msg4.value());System.out.println(msg4.what());} }案例三(獲取參數修飾注解對應的屬性值):
IsNotNull package com.huangting.annotation.P3;import java.lang.annotation.*;/*** 非空注解:使用在方法的參數上,false表示此參數可以為空,true不能為空*/ @Documented @Target({ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) public @interface IsNotNull {boolean value() default false; } Demo3 package com.huangting.annotation.P3;/*** 獲取參數修飾注解對應的屬性值*/ public class Demo3 {public void hello1(@IsNotNull(true) String name) {System.out.println("hello:" + name);}public void hello2(@IsNotNull String name) {System.out.println("hello:" + name);} } Demo3Test package com.huangting.annotation.P3; import org.junit.Test; import java.lang.reflect.Parameter; public class Demo3Test {@Testpublic void hello1() throws Exception {Demo3 demo3 = new Demo3();for (Parameter parameter : demo3.getClass().getMethod("hello1", String.class).getParameters()) {IsNotNull annotation = parameter.getAnnotation(IsNotNull.class);if(annotation != null){System.out.println(annotation.value());//true}}}@Testpublic void hello2() throws Exception {Demo3 demo3 = new Demo3();for (Parameter parameter : demo3.getClass().getMethod("hello2", String.class).getParameters()) {IsNotNull annotation = parameter.getAnnotation(IsNotNull.class);if(annotation != null){System.out.println(annotation.value());//false}}} }效果:
Aop自定義注解的應用
MyLog?
package com.huangting.annotation.Aop;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 MyLog {String desc(); } MyLogAspect package com.huangting.annotation.Aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component;@Component @Aspect public class MyLogAspect {private static final Logger logger = LoggerFactory.getLogger(MyLogAspect.class);/*** 只要用到了com.huangting.annotation.Aop.MyLog這個注解的,就是目標類*/@Pointcut("@annotation(com.huangting.annotation.Aop.MyLog)")private void MyValid() {}@Before("MyValid()")public void before(JoinPoint joinPoint) {MethodSignature signature = (MethodSignature) joinPoint.getSignature();MyLog myLog = signature.getMethod().getAnnotation(MyLog.class);System.out.println("[" + signature.getName() + " : start.....]");System.out.println("【目標對象方法被調用時候產生的日志,記錄到日志表中】:"+myLog.desc());} } BaseTestCase package com.huangting.annotation.Aop;import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations={"classpath:applicationContext.xml"}) public class BaseTestCase {} LogController package com.huangting.annotation.Aop;import org.springframework.stereotype.Component;@Component public class LogController {@MyLog(desc = "這是結合spring aop知識,講解自定義注解應用的一個案例")public void testLogAspect(){System.out.println("墻頭馬上遙相顧");} } LogControllerTest package com.huangting.annotation.Aop;import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired;public class LogControllerTest extends BaseTestCase {@Autowiredprivate LogController logController;@Testpublic void testLogAspect(){logController.testLogAspect();} }Joint Point
JointPoint是程序運行過程中可識別的點,這個點可以用來作為AOP切入點。JointPoint對象則包含了和切入相關的很多信息。比如切入點的對象,方法,屬性等。我們可以通過反射的方式獲取這些點的狀態和信息,用于追蹤tracing和記錄logging應用信息。
JointPoint使用詳解
這里詳細介紹JointPoint的方法,這部分很重要是coding核心參考部分。開始之前我們思考一下,我們到底需要獲取切入點的那些信息。我的思考如下
切入點的方法名字及其參數
切入點方法標注的注解對象(通過該對象可以獲取注解信息)
切入點目標對象(可以通過反射獲取對象的類名,屬性和方法名)
注:有一點非常重要,Spring的AOP只能支持到方法級別的切入。換句話說,切入點只能是某個方法。
針對以上的需求JDK提供了如下API————————————————
版權聲明:本文為CSDN博主「如何在5年薪百萬」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/kouryoushine/article/details/105299956
1 獲取切入點所在目標對象
Object targetObj =joinPoint.getTarget();# 可以發揮反射的功能獲取關于類的任何信息,例如獲取類名如下String className = joinPoint.getTarget().getClass().getName();因為一個類有很多方法,為了獲取具體切入點所在的方法可以通過如下API
2.獲取切入點方法的名字
getSignature());是獲取到這樣的信息 :修飾符+ 包名+組件名(類名) +方法名
這里我只需要方法名
String methodName = joinPoint.getSignature().getName()3. 獲取方法上的注解
方法1:xxxxxx是注解名字
Signature signature = joinPoint.getSignature();MethodSignature methodSignature = (MethodSignature) signature;Method method = methodSignature.getMethod();if (method != null){xxxxxx annoObj= method.getAnnotation(xxxxxx.class);}return null;方法2:上面我們已經知道了方法名和類的對象,通過反射可以獲取類的內部任何信息。
// 切面所在類Object target = joinPoint.getTarget();String methodName = joinPoint.getSignature().getName();Method method = null;for (Method m : target.getClass().getMethods()) {if (m.getName().equals(methodName)) {method = m;// xxxxxx annoObj= method.getAnnotation(xxxxxx.class);同上break;}}4. 獲取方法的參數
這里返回的是切入點方法的參數列表
Object[] args = joinPoint.getArgs();測試
@Target({ ElementType.PARAMETER, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ApiLog {/*** 模塊 */public String title() default "";/*** 日志記錄service實現* @return*/public String logService() default "operLogServiceImpl";/*** 是否保存請求的參數*/public boolean isSaveRequestData() default true;/*** 是否追蹤用戶操作* @return*/public boolean isTrack() default true; }切面類
package com.kouryoushine.aop.test; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method;@Aspect @Component public class DemoAspect {//切入點:aopdemo報下所有對象的save方法@Pointcut("execution(public * com.kouryoushine.aop.test.*.save*(..))")public void save(){}/*** 需要在update操作前后分別獲取更新前后的值* @param* @return*/@AfterReturning("save()")public void afterReturn(JoinPoint joinPoint) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {//1.獲取切入點所在目標對象Object targetObj =joinPoint.getTarget();System.out.println(targetObj.getClass().getName());// 2.獲取切入點方法的名字String methodName = joinPoint.getSignature().getName();System.out.println("切入方法名字:"+methodName);// 3. 獲取方法上的注解Signature signature = joinPoint.getSignature();MethodSignature methodSignature = (MethodSignature) signature;Method method = methodSignature.getMethod();if (method != null){ApiLog apiLog= method.getAnnotation(ApiLog.class);System.out.println("切入方法注解的title:"+apiLog.title());}//4. 獲取方法的參數Object[] args = joinPoint.getArgs();for(Object o :args){System.out.println("切入方法的參數:"+o);}}}服務類
@Service public class TestServcie {@ApiLog(title = "注解的標題",isSaveRequestData = false)public void save(String parm1,int parm2){System.out.println("執行目標對象的方法"+parm1+parm2);}public void update(){System.out.println("沒有注解的方法,不會被攔截");} }測試方法
@AutowiredTestServcie testServcie;@Testvoid test6() throws Exception{testServcie.save("參數1字符串",33);}測試結果
com.kouryoushine.aop.test.TestServcie 切入方法名字:save 切入方法注解的title:注解的標題 切入方法的參數:參數1字符串 切入方法的參數:33將方法參數值動態綁定到注解屬性上
@Cacheable注解作用,將帶有該注解方法的返回值存放到redis的的中;
使用方法在方法上使用@Cacheable(鍵=“測試+#P0 + P1#…”)
表示鍵值為測試+方法第一個參數+方法第二個參數,值為該方法的返回值。
以下源代碼表示獲取人員列表,Redis的中存放的關鍵值為’領袖’+ leaderGroupId + UUID + yearDetailId
創建的Java的注解@ExtCacheable
package com.huajie.annotation; 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 Cacheable { String key() default ""; String nextKey() default ""; int expireTime() default 1800;//30分鐘 }SpringAop切面CacheableAspect
package com.huajie.aspect; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; 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; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.huajie.annotation.ExtCacheable; import com.huajie.utils.RedisUtil;/*** redis緩存處理* 不適用與內部方法調用(this.)或者private*/ @Component @Aspect public class CacheableAspect { @Autowiredprivate RedisUtil redisUtil;@Pointcut("@annotation(com.huajie.annotation.Cacheable)")public void annotationPointcut() {}@Around("annotationPointcut()")public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {// 獲得當前訪問的classClass<?> className = joinPoint.getTarget().getClass();// 獲得訪問的方法名String methodName = joinPoint.getSignature().getName();// 得到方法的參數的類型Class<?>[] argClass = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();Object[] args = joinPoint.getArgs();String key = "";int expireTime = 1800;try {// 得到訪問的方法對象Method method = className.getMethod(methodName, argClass);method.setAccessible(true);// 判斷是否存在@ExtCacheable注解if (method.isAnnotationPresent(ExtCacheable.class)) {ExtCacheable annotation = method.getAnnotation(ExtCacheable.class);key = getRedisKey(args,annotation);expireTime = getExpireTime(annotation);}} catch (Exception e) {throw new RuntimeException("redis緩存注解參數異常", e);}// 獲取緩存是否存在boolean hasKey = redisUtil.hasKey(key);if (hasKey) {return redisUtil.get(key);} else {//執行原方法(java反射執行method獲取結果)Object res = joinPoint.proceed();//設置緩存redisUtil.set(key, res);//設置過期時間redisUtil.expire(key, expireTime);return res;}}private int getExpireTime(ExtCacheable annotation) {return annotation.expireTime();}private String getRedisKey(Object[] args,ExtCacheable annotation) {String primalKey = annotation.key();//獲取#p0...集合List<String> keyList = getKeyParsList(primalKey);for (String keyName : keyList) {int keyIndex = Integer.parseInt(keyName.toLowerCase().replace("#p", ""));Object parValue = args[keyIndex];primalKey = primalKey.replace(keyName, String.valueOf(parValue));}return primalKey.replace("+","").replace("'","");}// 獲取key中#p0中的參數名稱private static List<String> getKeyParsList(String key) {List<String> ListPar = new ArrayList<String>();if (key.indexOf("#") >= 0) {int plusIndex = key.substring(key.indexOf("#")).indexOf("+");int indexNext = 0;String parName = "";int indexPre = key.indexOf("#");if(plusIndex>0){indexNext = key.indexOf("#") + key.substring(key.indexOf("#")).indexOf("+");parName = key.substring(indexPre, indexNext);}else{parName = key.substring(indexPre);}ListPar.add(parName.trim());key = key.substring(indexNext + 1);if (key.indexOf("#") >= 0) {ListPar.addAll(getKeyParsList(key));}}return ListPar;} }?參考:
1、深入理解Java注解類型(@Annotation)_zejian_的博客-CSDN博客_java注解
2、java之aop使用及自定義注解 - 淪陷 - 博客園
3、SpringAOP中的JointPoint和ProceedingJoinPoint使用詳解(附帶詳細示例)_如何在5年薪百萬的博客-CSDN博客_proceedingjoinpoint有什么用
總結
以上是生活随笔為你收集整理的【Java注解系列】内置注解与AOP实现自定义注解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【计算机IO系列零】应用软件部分
- 下一篇: 中间件系列「三」netty之NIO基础