開發(fā)中這樣的代碼
對(duì)于每個(gè)開發(fā)人員都會(huì)遇到這樣情況伤靠,代碼如下:
@Api(tags = "自定義組合注解", description = "組合注解優(yōu)化代碼")
@StandardResult
@RequestMapping("/ccww")
@Controller
@ResponseBody
public class CombinationController{
}
在定義某個(gè)類或接口時(shí),使用了Spring自帶的注解(@Controller飘言、@Service峦睡,@Conditional),同時(shí)又要使用公司特定的注解標(biāo)注公司的業(yè)務(wù)嘉抒,接著就出現(xiàn)了以下處理方式的那一幕零聚。
”普通”開發(fā)人員
對(duì)于一般開發(fā)人員來說,只要功能、需求達(dá)到即可隶症,代碼也差不多就可以了,生活也就那樣政模。**只要不出現(xiàn)BUG,測試人員蚂会,產(chǎn)品經(jīng)理不找淋样,萬事大吉了!P沧 趁猴!
生活就像心電圖一樣,一帆風(fēng)順就證明掛了,
因此我們需要一顆折騰的心
高級(jí)”開發(fā)員(BUG工程師)
對(duì)于我們這類“高級(jí)”開發(fā)員(BUG工程師)彪见,看到這樣的代碼儡司,一個(gè)類上聲明了五六個(gè)注解,長長的一大串注解余指,代碼看起來就很普通捕犬,體現(xiàn)不出我們“高級(jí)”開發(fā)員(BUG工程師)折騰的心??,BUG創(chuàng)造師的水平酵镜。
而且注解本意是為了提供我們便捷碉碉,代碼優(yōu)化,作標(biāo)注用淮韭。但和XML一樣垢粮,過度使用就編程了一種災(zāi)難。
于是我們持著一顆折騰的心??缸濒,尋找一種可以讓自己看來舒服的整潔的代碼修正足丢,現(xiàn)在找到了使用組合注解的方式把需要的注解組合在一個(gè)自定義的組合注解上。
"高級(jí)”開發(fā)人員(BUG工程師)-基于組合注解方案
首先我們持著一個(gè)折騰的心??庇配,想到平時(shí)我們常見的SpringBoot的啟動(dòng)注解@SpringBootApplication斩跌,
為什么它一個(gè)注解可以實(shí)現(xiàn)那么多功能呢?
怎么實(shí)現(xiàn)的呢捞慌?
我們?cè)趺床拍茉趯⑦@個(gè)實(shí)現(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
注解袖订,聲明了它的類會(huì)默認(rèn)開啟自動(dòng)配置聲明
@ComponentScan
注解,同時(shí)是@ComponentScan
注解的容器嗅虏。我們發(fā)現(xiàn)scanBasePackages
和scanBasePackageClasses
兩個(gè)注解屬性上面同樣聲明了@AliasFor
注解洛姑,分別指向了@ComponentScan注解的basePackages
注解屬性和basePackageClasses
屬性。聲明了
exclude()
為排除特定的自動(dòng)配置類以及excludeName()
排除特定的自動(dòng)配置類名稱
疑問皮服?楞艾?
這時(shí)我們會(huì)想@SpringBootApplication為什么可以這樣使用呢参咙?
為什么可以繼承?
我們定義的普通類和接口有什么區(qū)別?
@AliasFor
為什么可以這樣使用硫眯?....
其實(shí)蕴侧,基于@Retention
的值可以分兩時(shí)期對(duì)注解進(jìn)行處理,:
編譯時(shí)處理
運(yùn)行時(shí)處理
@Retention的值為RetentionPolicy.SOURCE時(shí)注解只存在.java源文件中
@Retention的值為RetentionPolicy.CLASS時(shí)注解只存在于.class文件中两入,
都無法在運(yùn)行時(shí)去獲取注解的信息净宵,只能在編譯時(shí)處理
設(shè)定為RetentionPolicy.RUNTIME注解信息會(huì)存在.class文件中
通知單JVM加載.class文件時(shí)會(huì)把注解也加載到JVM中,所以就可在運(yùn)行時(shí)獲取注解的信息
運(yùn)行時(shí)處理:運(yùn)行時(shí)處理是通過反射機(jī)制獲取注解裹纳。
再看看最原始的注解(完全由元注解組成的):
public @Interface Annotation
{
}
創(chuàng)建注解使用@Interface類型聲明為注解择葡,并且聲明為@Interface類型的類在編譯時(shí)會(huì)自動(dòng)繼承Annotation接口。
那什么類型的類可以繼承接口呢剃氧?
自然是接口類型了刁岸,對(duì)編譯器而言,注解其實(shí)就是一個(gè)接口她我。所以,我們?cè)谀硞€(gè)類上聲明注解本質(zhì)上等價(jià)于繼承注解代表的接口迫横。因?yàn)樽⒔獗举|(zhì)上是接口番舆,就具有了相互繼承的能力了。
還有(心想矾踱,這問題少年恨狈,那么多問題),在注解里聲明的注解方法以及給注解屬性賦值怎么處理的呢呛讲?
對(duì)了禾怠,就是JDK動(dòng)態(tài)代理!編譯器把我們的類翻譯為實(shí)現(xiàn)了注解接口的類贝搁,然后用JDK動(dòng)態(tài)代理的方式攔截所有該類方法的調(diào)用吗氏,如果是自定義方法就放行;如果是注解方法就攔截下來執(zhí)行某些邏輯雷逆。至于我們給注解屬性賦值弦讽,會(huì)以JDK動(dòng)態(tài)代理類的常量的方式保存,需要時(shí)使用就可以了膀哲。我們可以看看代理注解的類是怎樣的往产,具體的可以實(shí)現(xiàn)跟蹤源碼看看
最后,@AliasFor的作用是什么呢某宪?
用到注解 屬性上仿村,表示兩個(gè)屬性互相為別名,互相為別名的屬性值必須相同兴喂,若設(shè)置成不同蔼囊,則會(huì)報(bào)錯(cuò)
注解是可以繼承的焚志,但是注解是不能繼承父注解的屬性的,也就是說,我在類掃描的時(shí)候,拿到的注解的屬性值,依然是父注解的屬性值,而不是你定義的注解的屬性值饵隙,所以此時(shí)可以在子注解對(duì)應(yīng)的屬性上加上@AliasFor
手動(dòng)起來齐遵,動(dòng)手優(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)化后贵少,整個(gè)類看起來輕簡了很多。以后碰到類似情況堆缘,可以考慮使用組合注解來進(jìn)行優(yōu)化, 將多個(gè)注解作用于一個(gè)注解滔灶,用一個(gè)注解就可以來實(shí)現(xiàn)那多個(gè)注解的功能,使作用的元素(即方法或類等)看上去更簡潔美觀吼肥,當(dāng)然主要還是更強(qiáng)大的屬性覆蓋功能录平。
但是也要視情況而定,不要盲目的使用組合注解缀皱,不然別人看起這代碼就比較費(fèi)勁了哈6氛狻!(“高級(jí)”程序員的微笑 )
溫馨提示:不要盲目的使用組合注解
優(yōu)點(diǎn):
- 代碼整齊
缺點(diǎn):
- 代碼可讀性比較差啤斗,想使用必須理解其原意
各位看官還可以嗎表箭?喜歡的話,動(dòng)動(dòng)手指點(diǎn)個(gè)贊??钮莲,點(diǎn)個(gè)關(guān)注唄C庾辍!謝謝支持崔拥!
也歡迎關(guān)注公眾號(hào)【Ccww筆記】极舔,原創(chuàng)技術(shù)文章第一時(shí)間推出