在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 {
}