註解式專案開發!詳細解析Java中各個註解的作用和使用方式

攻城獅Chova發表於2021-06-28

@Target

  • 作用: 指明瞭修飾的這個註解的使用範圍, 即被描述的註解可以用在哪裡
@Target(ElementType.Type)
  • ElementType取值的型別:
    • TYPE: 類,介面或者列舉
    • FIELD: 域,包含列舉常量
    • METHOD: 方法
    • PARAMETER: 引數
    • CONSTRUCTOR: 構造方法
    • LOCAL_VARIABLE: 區域性變數
    • ANNOTATION_TYPE: 註解型別
    • PACKAGE:

@Retention

  • 作用: 指明修飾的註解的生存週期, 即會保留到哪個階段
  • RetentionPolicy的取值型別有三種:
    • SOURCE: 原始碼級別保留,編譯後即丟棄
    • CLASS: 編譯級別保留,編譯後的class檔案中存在,在jvm執行時丟棄,這是預設值
    • RUNTIME: 執行級別保留,編譯後的class檔案中存在,在jvm執行時保留,可以被反射呼叫

@Documented

  • 作用: 指明修飾的註解,可以被例如javadoc此類的工具文件化
    • 只負責標記
    • 沒有成員取值

@Inherited

  • 作用: 允許子類繼承父類中的註解
  • @Inherited需要和@AliasFor一起使用: 在子註解對應的屬性使用@AliasFor
    • 註解是可以繼承的,但是註解是不能繼承父註解的屬性
    • 也就是說,在類掃描時的註解的屬性值依然是父註解的屬性值,而不是自定義註解的屬性值
    • 需要在註解的屬性上使用@AliasFor

@ComponentScan

  • 作用: 定義掃描的路徑從中找出標識了需要裝配的類自動裝配到spring的bean容器中
  • 預設會掃描該類所在的包下所有的配置類
  • @ComponentScan中的引數型別:
    • value: 用於對指定包的路徑進行掃描
    • basePackages: 用於指定包的路徑進行掃描,用法和value一樣.建議使用value
    • basePackageClasses: 用於對指定某個類的所在的包的路徑進行掃描
    • nameGenerator: 用於為Spring容器中的檢測到bean元件命名
    • useDefaultFilters: 是否開啟對 @Component,@Repository,@Service,@Controller的類進行檢測
    • excludeFilters: 按照過濾條件進行排除
      • FilterType.ANNOTATION: 按照註解
      • FilterType.ASSIGNABLE_TYPE: 按照給定的型別
      • FilterType.ASPECTJ: 使用ASPECTJ表示式
      • FilterType.REGEX: 使用正規表示式
      • FilterType.CUSTOM: 按照自定義規則
    • includeFilters: 按照過濾條件進行包含
      • FilterType.ANNOTATION: 按照註解
      • FilterType.ASSIGNABLE_TYPE: 按照給定的型別
      • FilterType.ASPECTJ: 使用ASPECTJ表示式
      • FilterType.REGEX: 使用正規表示式
      • FilterType.CUSTOM: 按照自定義規則

@Filter

  • 作用: 配置過濾條件的過濾器註解
  • @Filter中的引數型別:
    • type
    • class

@interface

  • 作用: 自定義註解
  • 自動繼承java.lang.annotation.Annotation介面,由編譯程式自動完成其他細節
  • 在定義註解時,不能繼承其他的註解或介面
  • @interface用來宣告一個註解:
    • 其中的每一個方法實際上是宣告一個配置引數
    • 方法的名稱就是引數的名稱
    • 方法的返回值型別就是引數的型別
    • 返回值型別只能是基本型別,Class,String,enum
    • 可以通過default來宣告引數的預設值
  • 定義註解的格式:
public @interface 註解名 {定義體}
  • 註解引數支援的資料型別:
    • 基本資料型別: int,float,boolean,byte,double,char,long,short
    • String型別
    • Class型別
    • enum型別
    • Annotation型別
    • 以上型別組合的陣列
  • Annotation型別中引數設定規則:
    • 只能用public或default預設訪問權修飾:
    • 引數成員只能用基本型別byte,short,char,int,long,float,double,boolean八種基本資料型別和String,Enum,Class,annotations等資料型別,以及這一些型別的陣列
    • 如果只有一個引數成員,最好把引數名稱設為value,後加小括號
  • 註解元素的預設值:
    • 註解元素必須有確定的值
    • 要麼在定義註解的預設值中指定,要麼在使用註解時指定,非基本型別的註解元素的值不可為null
    • 因此使用空字串或0作為預設值約束
  • 這個約束使得處理器很難表現一個元素的存在或缺失的狀態:
    • 因為每個註解的宣告中,所有元素都存在,並且都具有相應的值
  • 為了繞開這個約束,只能定義一些特殊的值(比如空字串或者負數),表示某個元素不存在

@AliasFor

  • 作用: 為註解的屬性新增別名
  • 在同一個註解內,對兩個不同的屬性一起使用,互為別名:
    • 無論為哪個屬性名設定屬性值,另一個屬性名也是同樣的屬性值
    • 互為別名的屬性值必須相同,否則會報錯
    • 屬性必須要有預設的屬性值
public @interface RequestMapping {
   
    @AliasFor("path") 			// 此時path和value值必須是一樣的,否則會報錯
    String[] value() default {};

    @AliasFor("value") 			// 此時path和value值必須是一樣的,否則會報錯
    String[] path() default {};
    
}
  • 顯式的覆蓋元註解中的屬性:
    • 顯式的為元註解的屬性設定別名
    • 屬性型別,屬性預設值必須相同
    • @AliasFor只能為作為當前註解的元註解起別名
  • 示例:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AopConfig.class)
public class AopUtilsTest {}

要想替換 @ContextConfiguration(classes = AopConfig.class) 註解,可以這樣定義一個標籤:

@Retention(RetentionPolicy.RUNTIME)
@ContextConfiguration
public @interface Context {
    @AliasFor(value = "classes", annotation = ContextConfiguration.class)
    Class<?>[] cs() default {};
}
  1. 因為 @ContextConfiguration註解本身被定義為 @Inherited的,所以Context註解即可理解為繼承 @ContextConfiguration註解
  2. cs屬性等同於 @ContextConfiguration屬性中的classes屬性.使用了 @AliasFor標籤,分別設定:
    1. value: 作為哪個屬性的別名
    2. annotation: 作為哪個註解的別名

使用Context標籤的可以達到同樣效果:

@RunWith(SpringJUnit4ClassRunner.class)
@STC(cs = AopConfig.class)
public class AopUtilsTest {}
  • 在一個註解中隱式宣告別名:
 @ContextConfiguration
 public @interface MyTestConfig {

    @AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
    String[] value() default {};

    @AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
    String[] groovyScripts() default {};

    @AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
    String[] xmlFiles() default {};
 }

這就是在統一註解中隱式宣告別名:

  1. MyTestConfig註解中 ,value,groovyScripts,xmlFiles都定義為@AliasFor(annotation = ContextConfiguration.class, attribute = "locations")的別名
  2. 在這個註解中 ,value,groovyScripts和xmlFiles也互為別名
  • 別名的傳遞:
    • @AliasFor註解是允許別名之間的傳遞的:
      • 如果A是B的別名,並且B是C的別名,那麼A是C的別名
@MyTestConfig
 public @interface GroovyOrXmlTestConfig {

    @AliasFor(annotation = MyTestConfig.class, attribute = "groovyScripts")
    String[] groovy() default {};

    @AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
    String[] xml() default {};
 }
  1. GroovyOrXmlTestConfig@MyTestConfig作為元註解
  2. 定義了groovy屬性,並作為MyTestConfig中的groovyScripts屬性的別名
  3. 定義了xml屬性,並作為ContextConfiguration中的locations屬性的別名
  4. 因為MyTestConfig中的groovyScripts屬性本身就是ContextConfiguration中的locations屬性的別名,所以xml屬性和groovy屬性也互為別名
  • @Alias中的屬性:
    • annotation: 類型別,別名屬性的類的型別,即別名的屬性屬於哪個註解類
    • attribute: 需要別名的屬性
    • value: 屬性的別名

@Import

  • @Import支援匯入普通的Java類,並宣告為一個Bean
  • @Import使用場景:
    • @Import主要用在基於Java程式碼顯式建立bean的過程中
    • @Import用於將多個分散的Java Config配置類融合成一個完整的config類
  • 配置類的組合主要發生在跨模組或者跨包的配置類引用過程中: 將多個按功能或者按業務劃分的配置檔案匯入到單個配置檔案中,避免將所有配置寫在一個配置中
  • @Import與@ImportResource註解的作用類似
  • 使用@ImportResource和@Value可以進行資原始檔的讀取

SpringBoot

@SpringBootApplication

  • 包含:
    • @Configuration
    • @EnableAutoConfiguration
    • @ComponentScan
  • 通常用在主類上

@ConfigurationProperties

  • 可以使用 @ConfigurationProperties獲取大量配置在application.propertiesapplication.yml中引數的引數值
  • @ConfigurationProperties的使用: 要為每個捕獲的外部屬性提供一個帶有欄位的類
    • 字首prefix定義的相關的外部屬性要繫結到類的欄位上
    • 根據SpringBoot寬鬆的繫結規則,類屬性的名稱必須與外部屬性名稱匹配
      • 可以將類型別的bean使用@Bean註解的方法注入到另一個bean中,那麼這個bean可以以型別安全的方式訪問外部配置的引數值
    • 可以簡單地用一個值初始化一個欄位來定義一個預設值. 最好與配置檔案中的值相同
    • 類本身可以是包私有的
    • 類的欄位必須有公共setter方法
  • 啟用@ConfigurationProperties:
    • 通過新增 @Component註解讓ComponentScan掃描到
    • 只有當該類所在的包被Spring的@ComponentScan掃描到才會生效.預設情況下,該註解會掃描在主應用類下所有包結構
    @Component
    @ConfigurationProperties(prefix = "spring.logger")
    class LogProperties {
    	...
    }
    
    • 通過SpringJava Configuration特性啟用@ConfigurationProperties
    • 只要MailModuleConfiguration類被SpringBoot應用掃描到,就可以在應用上下文中訪問MailModuleProperties bean
    @Configuration
    class PropertiesConfig {
    	@Bean 
    	public LogModuleProperties logModuleProperties() {
    		return new logModuleProperties();
    	}
    }
    
  • 同時可以使用 @EnableConfigurationProperties註解使得SpringBoot找到這個類. 這個註解用了 @Import(EnableConfigurationPropertiesImportSelector.class) 實現
@Configuration
@EnableConfigurationProperties(LogModuleProperties.class)
class PropertiesConfig {
}

啟用一個 @ConfigurationProperties類時最好模組化應用程式,並讓每個模組提供自己的 @ConfigurationProperties類,只提供模組需要的屬性.這樣可以使得在不影響其他模組的情況下重構一個模組中的屬性變得更加方便.因此不建議在程式類本身上使用 @EnableConfigurationProperties, 應該在特定模組的 @Configuration類上使用 @EnableConfigurationProperties, 該類也可以利用包私有的可見性對特定應用程式其餘部分隱藏屬性

  • @ConfigurationProerties中無法轉換的屬性:
    • 當為 @ConfigurationProperties中的屬性配置錯誤的值時,又不希望SpringBoot應用啟動失敗.可以設定ignoreInvalidFields註解屬性為true, 預設為false
@ConfigurationProperties(prefix = "spring.logger", ignoreInvalidFields = true)
public class LogModuleProperties {
	private Boolean enabled = Boolean.True;
}

SpringBoot將會設定enable欄位為設定好的預設值. 如果沒有設定預設值 ,enabled的值將為null, 因為這裡定義的是boolean的包裝類Boolean

  • @ConfigurationProperties中未知的屬性:
    • 預設情況下,SpringBoot會忽略不能繫結到 @ConfigurationProperties類欄位的屬性
    • 當配置檔案中又一個屬性實際沒有繫結到 @ConfigurationProperties類時,希望SpringBoot啟動失敗
    • 或者是以前使用過這個屬性,但已經被刪除了,希望被觸發告知手動從application.properties刪除這個屬性
    • 這是需要設定ignoreUnknownFields註解屬性為false, 預設為true
@ConfigurationProperties(prefix = "spring.logger", ignoreUnknownFields = false)
class LogModuleProperties {
	private Boolean enabled = Boolean.TRUE;
	private String defaultSubject;
}

對於ignoreUnkownFields, 在SpringBoot中可能有兩個帶有@ConfigurationProperties的類,同時繫結到了同一個名稱空間 (namespace) 上,其中一個類可能知道某個屬性,另一個類卻不知道某個屬性,這樣會導致啟動失敗.所以這個屬性不再使用

  • 啟動時校驗@ConfigurationProperties:
    • 如果希望配置引數在傳入到應用中是有效的,可以通過在欄位上新增bean validation註解,同時在類上新增 @Validated註解
@ConfigurationProperties(prefix = "spring.logger")
@Validated
@Data
class LogModuleProperties {
	@NotNull private Boolean enabled;
	@NotEmty private String defaultSubject;
} 

如果這些預設的驗證註解不能滿足驗證要求的,可以自定義註解. 如果驗證邏輯很特殊,可以實現一個方法,並用 @PostConstruct標記,如果驗證失敗,方法丟擲異常即可

  • 複雜屬性型別:
    • 多數情況下,傳遞給應用的引數是基本字串或者數字,有時又需要傳遞比如List的資料型別
    • List和Set:
      • 有兩種方式讓SpringBoot自動填充List屬性:
        • application.properties檔案中以陣列形式書寫
        spring.logger.smtpServers[0]=server1
        spring.logger.smtpServers[1]=server1
        
        • application.yml本身支援List型別,可以在application.yml檔案中新增
        spring:
        	mail:
        		smtpServers:
        			- server1
        			- server2
        
      • set集合也是使用同樣的配置方式
    • 推薦使用YML做資料配置,能夠更好的閱讀,層次分明
  • Duration:
    • SpringBoot內建支援從配置引數中解析duration(持續時間):
    @Data
    @ConfigurationProperties(prefix = "spring.logger")
    class loggerModuleProperties {
    	private Duration pauseBetweenLogs;
    }
    
    • 既可以配置毫秒數值,也可以配置帶有單位的文字:
    spring.logger.pause-between-logs=5s
    
    • 如果配置duration沒有寫單位,預設按照毫秒來指定,也可以通過 @DurationUnit來指定單位:
    @Data
    @ConfigurationProperties(prefix = "spring.logger")
    class loggerModuleProperties {
    	@DurationUnit(ChronoUnit.SECONDS)
    	private Duration pauseBetweenLogs;
    }
    
    • 常用單位如下:
      • ns: NANOSECONDS - 納秒
      • us: MICROSECONDS - 微秒
      • ms: MILLISECONDS - 毫秒
      • s: SECONDS - 秒
      • m: MINUTES - 分
      • h: HOURS - 時
      • d: DAYS - 天
  • DataSize:
    • 與Duration用法一樣,預設單位是byte(位元組) , 可以通過@DataSizeUnit單位指定
    @Data
    @ConfigurationProperties(prefix = "spring.logger")
    class loggerMailModuleProperties {
    	@DataSizeUnit(DataUnit.MEGABYTES)
    	private DataSize maxAttachmentSize = DataSize.ofMegebytes(2);
    } 
    
    • 新增配置:
    spring.logger.max-attachment-size=1MB
    
    輸出的結果都是以B(bytes) 為單位顯示的
    • 常見單位如下:
      • B: BYTES
      • KB: KILOBYTES
      • MB: MEGEBYTES
      • GB: GIGABYTES
      • TB: TERABYTES
  • 自定義型別:
    • 有時候,想解析配置引數到自定義的物件型別上,比如自定義物品重量:
    spring.logger.max-attachment-weight=5kg
    
    • MailModuleProeprties中新增Weight屬性:
    @Data
    @ConfigurationProperties(prefix = "spring.logger")
    class MailModuleProperties {
    	private Weight maxAttachmentWeight;
    }
    
    • 建立自定義轉換器converter:
    class WeightConverter implements Convert<String, Object> {
    	@Override
    	public Weight convert(String source) {
    		// 根據String型別的source建立並返回一個Weight型別的物件
    	}
    }
    
    • 將自定義轉換器converter註冊到SpringBoot上下文中
    @Configuration
    class PropertiesConfig {
    	@Bean
    	@ConfigurationPropertiesBinding
    	public WeightConverter weightConverter() {
    		return new WeightConverter();
    	}
    }
    
    @ConfigurationPropertiesBinding註解是讓SpringBoot使用該轉換器做資料繫結
  • 標記配置屬性為Deprecated:
@DeprecatedConfigurationProperty(reason = "change name", replacement = "none")
public String getDefaultSubject() {
	return defaultSubject;
}

可以通過新增 @DeprecatedConfigurationProperty註解到欄位的getter方法上,來標示該欄位為deprecated

  • SpringBoot@ConfigurationProperties註解在繫結型別安全的Java Bean時是非常強大的
  • 可以配合其註解屬性和 @DeprecatedConfigurationProperty註解讓配置更加模組化
  • 如果使用SpEL表示式,只能選擇 @Value註解

@Repository

  • 用於標註資料訪問元件,即DAO元件

@Service

  • 用於標註業務層元件

@RestController

  • 用於標註控制層元件
  • 包含:
    • @Controller
    • @ResponseBody

@Controller

  • 用於標註控制層元件
  • 需要返回頁面時要使用 @Controller而不是 @RestController

@ControllerAdvice

  • 用於定義 @ExceptionHandler, @InitBinder, @ModelAttribute, 並且應用到所有 @RequestMapping
    • @InitBinder: 在執行之前初始化資料繫結器
    • @ModelAttribute: 把值繫結到Model中,可以獲取到該值
    • @ExceptionHandler: 全域性異常捕捉處理

@Component

  • 泛指元件
  • 當元件無法歸類時,可以使用這個註解進行標註

@ResponseBody

  • 表示該方法的返回結果直接被寫入http response body
    • 一般在非同步獲取資料時使用
  • 在使用 @RequestMapping後,返回值通常解析為跳轉路徑
  • 比如:
    • 加上 @ResponseBody後返回結果不會被解析為跳轉路徑,而是直接寫入HTTP Response Body
    • 非同步獲取json資料,加上 @ResponseBody後,會直接返回json資料

@RequestBody

  • 引數前加上這個註解,表示該引數必填
  • 表示接收json字串轉為物件List

@ComponentScan

  • 元件掃描
  • 掃描到有 @Component, @Cotroller, @Service等這些註解的類,就會把這些類註冊為bean*

@Configuration

  • 表示該類是Bean的資訊源
  • 相當於XML中的,一般標註在主類上

@ConditionOnProperty

  • 控制Configuration在條件成立時生效
  • 屬性:
    • value: 陣列,獲取對應property的名稱,與name不可以同時使用
    • prefix: property名稱的字首,可有可無
    • name: 陣列 ,property完整名稱或者部分名稱(與prefix組合使用,組成完整的property名稱),不可以與value同時使用
    • havingValue: 可與name組合使用,比較獲取到的屬性值與havingValue給定的值是否相同,相同才載入配置
    • matchMissing: 缺少該property時是否可以載入. 如果為true, 沒有該property也會正常載入. 如果為false, 則沒有該property時則會報錯,預設為false
    • relaxedNames: 是否支援鬆散匹配

@Bean

  • 相當於XML中的,標註在方法上
  • 表示生成一個bean, 並交給Spring管理

@EnableAutoConfiguration

  • 使SpringBoot根據應用所宣告的依賴來對Spring框架進行配置
  • 一般加在主類上

@Autowired

  • byType方式
  • 使用已經配置好的Bean, 完成屬性,方法的組裝
  • 可以對類成員,方法以及建構函式進行標註,完成自動裝配的工作
  • 如果加上 @Autowired(required = false), 當找不到bean時也不會報錯

@Qualifier

  • 當有多個同一型別的Bean時,可以使用 @Qualifier("name") 來指定
  • 需要和 @Autowired一起使用

@Resource

  • @Resource(name = "name", type = "type")
  • 如果沒有屬性的話,預設為byName,@Autowired功能類似

@RequestMapping

  • @RequestMapping是一個用來處理請求地址對映的註解,可以使用在類或者方法上
  • 用在類上時,表示類中所有響應請求的方法都以該地址作為父路徑
  • @RequestMapping有六個屬性:
    • params: 指定request中必須包含某些引數值,才讓該方法處理請求
    • headers: 指定request中必須包含某些指定的header值,才能讓該方法處理請求
    • value: 指定請求的實際地址. 指定的地址可以是URI Template模式
    • method: 指定請求的method型別 ,GET, POST, PUT,DELETE
    • consumes: 指定處理請求的提交內容型別 - Content-Type. 比如: application,json,text,html
    • produces: 指定返回的內容型別,僅當request請求頭中的 (Accept) 型別中包含該指定型別才返回

@GetMapping

  • @GetMapping, @PostMapping是組合註解
  • 相當於 @RequestMapping(value = "/", method = RequestMethod.Get(Post, Put, Delete))

@RequestParam

  • 用在方法的引數前面
  • 相當於 request.getParameter

@PathVariable

  • 路徑變數: RequestMapping("user/get/mac/{macAddress}")
public String getByMacAddress(@PathVariable("macAddress") String macAddress) {}

引數與大括號裡的名字相同的話,註解後括號裡的內容可以不填

全域性異常處理

@ControllerAdvice

  • 包含 @Component
  • 可以被掃描到
  • 統一異常處理

@ExceptionHandler

  • @Exceptionhandler(Exception.class)
    • 用在方法上面,表示遇到這個異常就執行這個方法

SpringCloud

@EnableEurekaServer

  • 用在SpringBoot啟動類上
  • 表示這是一個Eureka服務註冊中心

@EnableDiscoveryClient

  • 用在SpringBoot啟動類上
  • 表示這是一個服務,可以被註冊中心找到

@LoadBalanced

  • 開啟負載均衡能力

@EnableCircuitBreaker

  • 用在SpringBoot啟動類上
  • 開啟斷路器功能

HystrixCommand

  • @HystrixCommand(fallbackMethod = "backMethod")
  • 用在方法上,表示fallbackMethod指定斷路回撥方法

@EnableConfigServer

  • 用在SpringBoot啟動類上
  • 表示這是一個配置中心,開啟Config Server

@EnableZuulProxy

  • 用在SpringBoot啟動類上
  • 表示開啟zuul路由

@SpringCloudApplication

  • 微服務註解集合,包含:
    • @SpringBootApplication: SpringBoot註解
    • @EnableDiscoveryClient: 註冊服務中心Eureka註解
    • @EnableCircuitBreaker: 斷路器註解
  • 這是每一個微服務必須應該有的註解

相關文章