【如何讓代碼變“高級”(一)】-Spring組合注解提升代碼維度


原創不易,點個贊💗,支持支持

生活就像心電圖一樣,一帆風順就證明掛了,

因此我們需要一顆折騰的心💗

開發中這樣的代碼

對於每個開發人員都會遇到這樣情況,代碼如下:

@Api(tags = "自定義組合注解", description = "組合注解優化代碼")
@StandardResult
@RequestMapping("/ccww")
@Controller
@ResponseBody
public class CombinationController{
​
}

 

在定義某個類或接口時,使用了Spring自帶的注解(@Controller、@Service,@Conditional),同時又要使用公司特定的注解標注公司的業務,接着就出現了以下處理方式的那一幕:

”普通”開發人員

 對於一般開發人員來說,只要功能、需求達到即可,代碼也差不多就可以了,生活也就那樣。只要不出現BUG,測試人員,產品經理不找,萬事大吉了!!!

“高級”開發員(BUG工程師)

對於我們這類“高級”開發員(BUG工程師),看到這樣的代碼,一個類上聲明了五六個注解,長長的一大串注解,代碼看起來就很普通,體現不出我們“高級”開發員(BUG工程師)折騰的心💗,BUG創造師的水平

而且注解本意是為了提供我們便捷,代碼優化,作標注用。但和XML一樣,過度使用就編程了一種災難。

於是我們持着一顆折騰的心💗,尋找一種可以讓自己看來舒服的整潔的代碼修正,現在找到了使用組合注解的方式把需要的注解組合在一個自定義的組合注解上。

 

"高級”開發人員(BUG工程師)-基於組合注解方案

首先我們持着一個折騰的心💗,想到平時我們常見的SpringBoot的啟動注解@SpringBootApplication

1. 為什么它一個注解可以實現那么多功能呢?

2. 怎么實現的呢?

3. 我們怎么才能在將這個實現轉化出自己所需的自定義注解呢?

接下來我們看看它的源碼:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
        @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
​
    @AliasFor(annotation = EnableAutoConfiguration.class)
    Class<?>[] exclude() default {};
    
​
    @AliasFor(annotation = EnableAutoConfiguration.class)
    String[] excludeName() default {};
    
​
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};
    
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
    Class<?>[] scanBasePackageClasses() default {};
​
}

@SpringBootApplication功能列表:

  • 聲明@Inherited注解,聲明了它的類的子類是可以繼承它的

  • 聲明@SpringBootConfiguration注解,可為類標注為配置類

  • 聲明@EnableAutoConfiguration注解,聲明了它的類會默認開啟自動配置

  • 聲明@ComponentScan注解,同時是@ComponentScan注解的容器。我們發現scanBasePackagesscanBasePackageClasses兩個注解屬性上面同樣聲明了@AliasFor注解,分別指向了@ComponentScan注解的basePackages注解屬性和basePackageClasses屬性。

  • 聲明了exclude()為排除特定的自動配置類以及excludeName()排除特定的自動配置類名稱

 

疑問??

  • 這時我們會想@SpringBootApplication為什么可以這樣使用呢?

  • 為什么可以繼承?

  • 我們定義的普通類和接口有什么區別?

  • @AliasFor 為什么可以這樣使用?

  • ....

其實,基於@Retention的值可以分兩時期對注解進行處理,:

  1. 編譯時處理

  2. 運行時處理

@Retention的值為RetentionPolicy.SOURCE時注解只存在.java源文件中

@Retention的值為RetentionPolicy.CLASS時注解只存在於.class文件中,

都無法在運行時去獲取注解的信息,只能在編譯時處理

 

設定為RetentionPolicy.RUNTIME注解信息會存在.class文件中

通知單JVM加載.class文件時會把注解也加載到JVM中,所以就可在運行時獲取注解的信息

運行時處理:運行時處理是通過反射機制獲取注解

 

再看看最原始的注解(完全由元注解組成的):

public @Interface Annotation
{
​
}

創建注解使用@Interface類型聲明為注解,並且聲明為@Interface類型的類在編譯時會自動繼承Annotation接口。

那什么類型的類可以繼承接口呢?

自然是接口類型了,對編譯器而言,注解其實就是一個接口。所以,我們在某個類上聲明注解本質上等價於繼承注解代表的接口。因為注解本質上是接口,就具有了相互繼承的能力了。

還有(心想,這問題少年,那么多問題),在注解里聲明的注解方法以及給注解屬性賦值怎么處理的呢?

對了,就是JDK動態代理!編譯器把我們的類翻譯為實現了注解接口的類,然后用JDK動態代理的方式攔截所有該類方法的調用,如果是自定義方法就放行;如果是注解方法就攔截下來執行某些邏輯。至於我們給注解屬性賦值,會以JDK動態代理類的常量的方式保存,需要時使用就可以了。我們可以看看代理注解的類是怎樣的,具體的可以實現跟蹤源碼看看

最后,@AliasFor的作用是什么呢?

  • 用到注解 屬性上,表示兩個屬性互相為別名,互相為別名的屬性值必須相同,若設置成不同,則會報錯

  • 注解是可以繼承的,但是注解是不能繼承父注解的屬性的,也就是說,我在類掃描的時候,拿到的注解的屬性值,依然是父注解的屬性值,而不是你定義的注解的屬性值,所以此時可以在子注解對應的屬性上加上@AliasFor

 

手動起來,動手優化

現在該發揮我們程序員的核心本領之一“仿寫”,有什么是我們不能仿寫出來的呢?

哈哈( 程序員的傲嬌😏 ),話不多說,擼起袖子,開碼,仿寫@SpringBootApplication注解:

/**
*自定義組合注解
*@author Ccww
*
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
@RequestMapping
@Api
public @interface StandardResultRestControllerApi
{
    /**
    * 定義映射路徑URL
    * @return
    */
   @AliasFor(annotation = RequestMapping.class, value = "path")
   String[] value() default {};
​
    /**
    *定義spring類名稱
    *@return
    */
    @AliasFor(annotation = Controller.class, value = "value")
    String name() default "";
    /**
    *定義Api類tags屬性
    *@return
    */
    @AliasFor(annotation = Api.class, attribute = "tags")
    String[] tags() default "";
    
    /**
    *定義Api類description屬性
    *@return
    */
    @AliasFor(annotation = Api.class, attribute = "description")
    String description() default "";
}

優化后的代碼如下

@StandardResultRestControllerApi(path="/Combination",tags = "自定義組合注解", description = "組合注解優化代碼")
public class CombinationController
{
    
}

總結

經過優化后,整個類看起來輕簡了很多。以后碰到類似情況,可以考慮使用組合注解來進行優化, 將多個注解作用於一個注解,用一個注解就可以來實現那多個注解的功能,使作用的元素(即方法或類等)看上去更簡潔美觀,當然主要還是更強大的屬性覆蓋功能。

但是也要視情況而定,不要盲目的使用組合注解,不然別人看起這代碼就比較費勁了哈!!( “高級”程序員的微笑🙂 )

溫馨提示:不要盲目的使用組合注解

優點:

  • 代碼整齊

缺點:

  • 代碼可讀性比較差,想使用必須理解其原意

各位看官還可以嗎?喜歡的話,動動手指點個贊💗,點個關注唄!!謝謝支持!

也歡迎關注公眾號【Ccww筆記】,原創技術文章第一時間推出


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM