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

開發(fā)中這樣的代碼

對于每個開發(fā)人員都會遇到這樣情況,代碼如下:

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

}

在定義某個類或接口時,使用了Spring自帶的注解(@Controller、@Service,@Conditional),同時又要使用公司特定的注解標(biāo)注公司的業(yè)務(wù),接著就出現(xiàn)了以下處理方式的那一幕。

”普通”開發(fā)人員

對于一般開發(fā)人員來說,只要功能、需求達(dá)到即可,代碼也差不多就可以了,生活也就那樣。**只要不出現(xiàn)BUG,測試人員,產(chǎn)品經(jīng)理不找,萬事大吉了?。。?/p>

生活就像心電圖一樣,一帆風(fēng)順就證明掛了,
因此我們需要一顆折騰的心

image-20191128104802646.png

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

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

而且注解本意是為了提供我們便捷,代碼優(yōu)化,作標(biāo)注用。但和XML一樣,過度使用就編程了一種災(zāi)難。


image-20191128105041607.png

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

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

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

為什么它一個注解可以實現(xiàn)那么多功能呢?
怎么實現(xiàn)的呢?
我們怎么才能在將這個實現(xiàn)轉(zhuǎn)化出自己所需的自定義注解呢?

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

@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注解,可為類標(biāo)注為配置類

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

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

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

疑問??

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

  • 為什么可以繼承?

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

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

  • ....

其實,基于@Retention的值可以分兩時期對注解進(jìn)行處理,:

  1. 編譯時處理

  2. 運(yùn)行時處理

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

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

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

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

運(yùn)行時處理:運(yùn)行時處理是通過反射機(jī)制獲取注解

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

public @Interface Annotation
{

}

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

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

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

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

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

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

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

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

手動起來,動手優(yōu)化

現(xiàn)在該發(fā)揮我們程序員的核心本領(lǐng)之一“仿寫”,有什么是我們不能仿寫出來的呢?哈哈( 程序員的傲嬌 ),話不多說,擼起袖子,開碼,仿寫@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 "";
}

優(yōu)化后的代碼如下

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

總結(jié)

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

但是也要視情況而定,不要盲目的使用組合注解,不然別人看起這代碼就比較費(fèi)勁了哈!?。ā案呒墶背绦騿T的微笑 )

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

優(yōu)點(diǎn):

  • 代碼整齊

缺點(diǎn):

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

各位看官還可以嗎?喜歡的話,動動手指點(diǎn)個贊??,點(diǎn)個關(guān)注唄??!謝謝支持!
也歡迎關(guān)注公眾號【Ccww筆記】,原創(chuàng)技術(shù)文章第一時間推出

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容