Spring Boot實戰小技巧(七):實現自定義註解

在Spring Boot的開發中,經常會有與其他業務系統對接或封裝公共API接口等需求,爲了使開發過程更加便捷性,並降低系統之間的耦合性,通常會使用自定義註解來封裝一些業務,在後續開發時直接使用自定義註解即可,無需再關注註解內部業務流程的實現。本文將介紹如何在Spring Boot框架中實現自定義註解。

1 添加依賴

<!-- 引入aop -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2 創建註解類
項目結構如下:
在這裏插入圖片描述
創建annotation包(自定義名稱),在該包下創建註解類,右鍵annotation包,選擇添加Java Class。此類的類型爲@interface,在Kind處選擇Annotation。

註解類常用註解的作用如下:

註解 參數類型 作用
@Target ElementType 指定寫入註解的合法位置。
@Retention RetentionPolicy 指定註解的生存週期。
@Documented 表明由 javadoc記錄,註釋成爲公共API的一部分。
@Inherited 允許子類繼承父類中的註解。

常見的註解使用目標位置爲:類、字段、方法等,ElementType枚舉類型的定義如下:

public enum ElementType {
    /** 類, 接口 (包括註釋類型), 或 枚舉 聲明 */
    TYPE,
 
    /** 字段聲明(包括枚舉常量) */
    FIELD,
 
    /** 方法聲明(Method declaration) */
    METHOD,
 
    /** 正式的參數聲明 */
    PARAMETER,
 
    /** 構造函數聲明 */
    CONSTRUCTOR,
 
    /** 局部變量聲明 */
    LOCAL_VARIABLE,
 
    /** 註釋類型聲明 */
    ANNOTATION_TYPE,
 
    /** 包聲明 */
    PACKAGE,
 
    /**
     * 類型參數聲明
     *
     * @since 1.8
     */
    TYPE_PARAMETER,
 
    /**
     * 使用的類型
     *
     * @since 1.8
     */
    TYPE_USE
}

RetentionPolicy枚舉類型的定義如下:

public enum RetentionPolicy {
    /**
     * 註釋只在源代碼級別保留,編譯時被忽略
     */
    SOURCE,
    /**
     * 註釋將被編譯器在類文件中記錄
     * 但在運行時不需要JVM保留。這是默認的
     * 行爲.
     */
    CLASS,
    /**
     *註釋將被編譯器記錄在類文件中
     *在運行時保留VM,因此可以反讀。
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}

註解類示例代碼如下:

@Target({ElementType.METHOD, ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyCache {

    String name() default "";
    String action() default "";
    String key() default "";
    boolean filter() default false;
}

3 創建切面類
實現方法的註解時,除註解類外還需要創建切面類,用於實現被註解方法執行前後需要執行的業務邏輯。創建aspect包(自定義名稱),在該包下創建切面類,右鍵aspect包,選擇添加Aspect,Kind選擇@Aspect。或直接創建一個Java Class,然後在類上方添加@Aspect註解。

切面類示例代碼如下:

@Aspect
@Component
public class MyCacheAspect {
 
    /**
     * 定義切點
     */
    @Pointcut("@annotation(com.cetc.cloud.annotation.MyCache)")
    public void annotationPointcut() {
    }
 
    /**
     * 方法開始執行之前
     */
    @Before("annotationPointcut()")
    public void doBeforeRunning (JoinPoint joinPoint) {
    
    }
 
 	/**
     * 方法開始執行之後
     */
    @After("annotationPointcut()")
    public void doAfterRunning (JoinPoint joinPoint) {
    
    }
 
 	/**
     * 當方法執行完返回結果時
     * returning屬性指定方法參數的result攔截返回結果
     * 可獲取或修改返回結果
     */
    @AfterReturning(value = "annotationPointcut()", returning = "result")
    public void doAfterReturning (JoinPoint joinPoint, Object result) {
    
    }
 
 	/**
     * 當方法執行異常時
     * throwing屬性指定方法參數的e獲取異常參數
     * 可查看異常信息
     */
    @AfterThrowing(value = "annotationPointcut()", throwing = "e")
    public void doAfterThrowing (JoinPoint joinPoint, Exception e) {
    
    }
 
 	/**
     * 切點環繞通知
     * 可跳過執行原方法
     */
    @Around("annotationPointcut()")
    public Object aroundPointcut (ProceedingJoinPoint joinPoint) throws Throwable {
        //原方法執行之前
        ...
        //執行原方法,可跳過
        Object obj = joinPoint.proceed();
        //原方法執行之後
        ...
        //返回方法執行結果
        return obj;
    }
}

實現註解類和切面類之後,就可以開始使用自定義的方法註解了。示例如下:

	@Override
    @MyCache(name = "userCache", action = "query", key = "#user.account", filter = false)
    public User getUserByAccount(User user) {
        ...
    }

4 方法註解的執行順序
切面類中幾種註解的運行順序如下:
@Around
@Before
原方法
@Around
@After
@AfterThrowing
@AfterReturning

當一個方法上有多個註解時,默認按照從上到下的順序設置註解的Order,即註解執行順序,Order小的註解先被觸發。

註解即Spring AOP,Spring AOP基於面向切面編程思想,即所有註解和方法是一個同心圓,要執行的方法爲圓心。最外層的Order最小,從最外層按照AOP1、AOP2的順序依次執行Around方法、Before方法,然後執行原方法,最後按照AOP2、AOP1的順序依次執行Around方法、After方法、AfterThrow方法、AfterReturn方法。也就是說對於多個AOP來說,先執行Before的,一定後執行After。見下圖:
在這裏插入圖片描述
也可以自己指定AOP的執行順序Order,在切面上使用@Order註解,如:

@Aspect
@Component
@Order(1)
public class Aspect1 {
 
}
@Aspect
@Component
@Order(2)
public class Aspect2 {
 
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章