第十二章:Spring 國際化
Spring 國際化使用場景
- 普通國際化文案
- Bean Validation 校驗國際化文案
- Web 站點頁面渲染
- Web MVC 錯誤消息提示
Spring 國際化接口
- 核心接口 -
org.springframework.context.MessageSource - 主要概念
- 文案模板編碼(code)
- 文案模板參數(args)
- 區域(Locale)
層次性 MessageSource
- Spring 層次性接口回顧
org.springframework.beans.factory.HierarchicalBeanFactoryorg.springframework.context.ApplicationContextorg.springframework.beans.factory.config.BeanDefinition
- Spring 層次性國際化接口
org.springframework.context.HierarchicalMessageSource
Java 國際化標准實現
核心接口
- 抽象類實現 -
java.util.ResourceBundle- Properties 資源實現 -
java.util.PropertyResourceBundle - 例舉實現 -
java.util.ListResourceBundle
- Properties 資源實現 -
ResourceBundle 核心特性
- Key-Value 設計
- 鍵唯一地標識了包中特定於語言環境的對象
- value 就是 文案模板編碼(code)
- 層次性設計
- 緩存設計
- 字符編碼控制 -
java.util.ResourceBundle.Control(@since 1.6) - Control SPI 擴展 -
java.util.spi.ResourceBundleControlProvider(@since 1.8)
Java 文本格式化
核心接口- java.text.MessageFormat
- 基本用法
- 設置消息格式模式-
new MessageFormat(...) - 格式化 -
format(new Object[]{...})
- 設置消息格式模式-
- 消息格式模式
- 格式元素:
{ArgumentIndex (,FormatType,(FormatStyle))} FormatType:消息格式類型,可選項,每種類型在number、date、time和choice類型選其一FormatStyle:消息格式風格,可選項,包括:short、medium、long、full、integer、currency、percent
- 格式元素:
高級特性
- 重置消息格式模式
- 重置
java.util.Locale - 重置
java.text.Format
MessageSource 開箱即用實現
- 基於
ResourceBundle+MessageFormat組合MessageSource實現org.springframework.context.support.ResourceBundleMessageSource
- 可重載
Properties+MessageFormat組合MessageSource實現org.springframework.context.support.ReloadableResourceBundleMessageSource
MessageSource 內建依賴
MessageSource 內建 Bean 可能來源
- 預注冊 Bean 名稱為:
messageSource,類型為:MessageSource - 默認內建實現 -
DelegatingMessageSource- 層次性查找
MessageSource對象
- 層次性查找
課外資料
Spring Boot 為什么要新建 MessageSource Bean?
AbstractApplicationContext的實現決定了MessageSource內建實現- Spring Boot 通過外部化配置簡化
MessageSourceBean 構建 - Spring Boot 基於 Bean Validation 校驗非常普遍
SpringBoot 中關於 MessageSource 的自動配置類
org.springframework.context.support.ResourceBundleMessageSource
面試題
Spring 國際化接口有哪些?
- 核心接口 -
MessageSource - 層次性接口 -
org.springframework.context.HierarchicalMessageSource
Spring 有哪些 MessageSource 內建實現?
org.springframework.context.support.ResourceBundleMessageSourceorg.springframework.context.support.ReloadableResourceBundleMessageSourceorg.springframework.context.support.StaticMessageSourceorg.springframework.context.support.DelegatingMessageSource
如何實現配置自動更新 MessageSource?
主要技術
- Java NIO 2:
java.nio.file.WatchService - Java Concurrency :
java.util.concurrent.ExecutorService - Spring:
org.springframework.context.support.AbstractMessageSource
第十三章:Spring 校驗
Spring 校驗使用場景
- Spring 常規校驗(Validator)
- Spring 數據綁定(DataBinder)
- Spring Web 參數綁定(WebDataBinder)
- Spring Web MVC / Spring WebFlux 處理方法參數校驗
Validator 接口設計
-
org.springframework.validation.Validator -
接口職責
- Spring 內部校驗器接口,通過編程的方式校驗目標對象
-
核心方法
supports(Class):校驗目標類能否校驗validate(Object,Errors):校驗目標對象,並將校驗失敗的內容輸出至Errors對象
-
配套組件
- 錯誤收集器:
org.springframework.validation.Errors - Validator 工具類:
org.springframework.validation.ValidationUtils
- 錯誤收集器:
Errors 接口設計
org.springframework.validation.Errors- 接口職責
- 數據綁定和校驗錯誤收集接口,與 Java Bean 和其屬性有強關聯性
- 核心方法
reject方法(重載):收集錯誤文案rejectValue方法(重載):收集對象字段中的錯誤文案
- 配套組件
- Java Bean 錯誤描述:
org.springframework.validation.ObjectError - Java Bean 屬性錯誤描述:
org.springframework.validation.FieldError
- Java Bean 錯誤描述:
Errors 文案來源
Errors 文案生成步驟
- 選擇
Errors實現(如:org.springframework.validation.BeanPropertyBindingResult) - 調用
reject或rejectValue方法 - 獲取
Errors對象中ObjectError或FieldError - 將
ObjectError或FieldError中的code和args,關聯MessageSource實現(如:ResourceBundleMessageSource)
Errors 不能直接生成文案,但是可以提供國際化接口 MessageSource 所需要的 code 和 args
自定義 Validator
實現 org.springframework.validation.Validator 接口
- 實現
supports方法 - 實現
validate方法- 通過
Errors對象收集錯誤ObjectError:對象(Bean)錯誤:FieldError:對象(Bean)屬性(Property)錯誤
- 通過
ObjectError和FieldError關聯MessageSource實現獲取最終文案
- 通過
Validator 的救贖
Bean Validation 與 Validator 適配
- 核心組件 -
org.springframework.validation.beanvalidation.LocalValidatorFactoryBean - 依賴 Bean Validation - JSR-303 or JSR-349 provider
- Bean 方法參數校驗 -
org.springframework.validation.beanvalidation.MethodValidationPostProcessor
關聯注解:
org.springframework.validation.annotation.Validatedjavax.validation.Valid
public class SpringBeanValidationDemo {
public static void main(String[] args) {
// 配置 XML 配置文件
// 啟動 Spring 應用上下文
ConfigurableApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/bean-validation-context.xml");
// Validator validator = applicationContext.getBean(Validator.class);
// System.out.println(validator instanceof LocalValidatorFactoryBean);
UserProcessor userProcessor = applicationContext.getBean(UserProcessor.class);
userProcessor.process(new User());
// 關閉應用上下文
applicationContext.close();
}
@Component
@Validated
static class UserProcessor {
public void process(@Valid User user) {
System.out.println(user);
}
}
static class User {
@NotNull
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
}
<context:component-scan base-package="org.geekbang.thinking.in.spring.validation"/>
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">
<property name="validator" ref="validator"/>
</bean>
面試題
Spring 校驗接口是哪個?
org.springframework.validation.Validator
Spring 有哪些校驗核心組件?
- 檢驗器:
org.springframework.validation.Validator - 錯誤收集器:
org.springframework.validation.Errors - Java Bean 錯誤描述:
org.springframework.validation.ObjectError - Java Bean 屬性錯誤描述:
org.springframework.validation.FieldError - Bean Validation 適配:
org.springframework.validation.beanvalidation.LocalValidatorFactoryBean
第十四章:Spring 數據綁定
Spring 數據綁定使用場景
- Spring BeanDefinition 到 Bean 實例創建
- Spring 數據綁定(DataBinder)
- Spring Web 參數綁定(WebDataBinder)
Spring 數據綁定組件
- 標准組件
org.springframework.validation.DataBinder
- Web 組件
org.springframework.web.bind.WebDataBinderorg.springframework.web.bind.ServletRequestDataBinderorg.springframework.web.bind.support.WebRequestDataBinderorg.springframework.web.bind.support.WebExchangeDataBinder(since 5.0)
DataBinder 核心屬性
| 屬性 | 說明 |
|---|---|
| target | 關聯目標 Bean |
| objectName | 目標 Bean名稱 |
| bindingResult | 屬性綁定結果 |
| typeConverter | 類型轉換器 |
| conversionService | 類型轉換服務 |
| messageCodesResolver | 校驗錯誤文案 Code 處理器 |
| validators | 關聯的 Bean Validator 實例集合 |
DataBinder 綁定方法
bind(PropertyValues):將PropertyValuesKey-Value 內容映射到關聯 Bean(target)中的屬性上- 假設
PropertyValues中包含 “name = 小馬哥” 的鍵值對,同時 Bean 對象 User 中存在 name 屬性,當 bind 方法執行時,User 對象中的 name 屬性值將被綁定為“小馬哥”
Spring 數據綁定元數據
DataBinder 元數據 - PropertyValues
| 特征 | 說明 |
|---|---|
| 數據來源 | BeanDefinition,主要來源 XML 資源配置 BeanDefinition |
| 數據結構 | 由一個或多個 PropertyValue 組成 |
| 成員結構 | PropertyValue 包含屬性名稱,以及屬性值(包括原始值、類型轉換后的值) |
| 常見實現 | MutablePropertyValues |
| Web 擴展實現 | ServletConfigPropertyValues、ServletRequestParameterPropertyValues |
| 相關生命周期 | InstantiationAwareBeanPostProcessor#postProcessProperties |
Spring 數據綁定控制參數
DataBinder 綁定特殊場景分析
- 當
PropertyValues中包含名稱 x 的PropertyValue,目標對象 B 不存在 x 屬性,當 bind 方法執行時,會發生什么?- 默認忽略未知的屬性,
ignoreUnknownFields
- 默認忽略未知的屬性,
- 當
PropertyValues中包含名稱 x 的PropertyValue,目標對象 B 中存在 x 屬性,當 bind 方法執行時,如何避免 B 屬性 x 不被綁定?- 設置
disallowedFields屬性
- 設置
- 當
PropertyValues中包含名稱 x.y 的PropertyValue,目標對象 B 中存在 x 屬性(嵌套 y 屬性),當 bind 方法執行時,會發生什么?- 默認支持嵌套屬性,
autoGrowNestedPaths
- 默認支持嵌套屬性,
DataBinder 綁定控制參數
| 參數名稱 | 說明 |
|---|---|
| ignoreUnknownFields | 是否忽略未知字段,默認值:true |
| ignoreInvalidFields | 是否忽略非法字段,默認值:false |
| autoGrowNestedPaths | 是否自動增加嵌套路徑,默認值:true |
| allowedFields | 綁定字段白名單 |
| disallowedFields | 綁定字段黑名單 |
| requiredFields | 必須綁定字段 |
BeanWrapper 的使用場景
BeanWrapper
- Spring 底層 JavaBeans 基礎設施的中心化接口
- 通常不會直接使用,間接用於
BeanFactory和DataBinder - 提供標准 JavaBeans 分析和操作,能夠單獨或批量存儲 Java Bean 的屬性(properties)
- 支持嵌套屬性路徑(nested path)
- 實現類
org.springframework.beans.BeanWrapperImpl
Spring 底層 Java Beans 替換實現
- JavaBeans 核心實現 -
java.beans.BeanInfo- 屬性(Property)
java.beans.PropertyEditor
- 方法(Method)
- 事件(Event)
- 表達式(Expression)
- 屬性(Property)
- Spring 替代實現 -
org.springframework.beans.BeanWrapper- 屬性(Property)
java.beans.PropertyEditor
- 嵌套屬性路徑(nested path)
- 屬性(Property)
課外資料
標准 JavaBeans 是如何操作屬性的?
| API | 說明 |
|---|---|
| java.beans.Introspector | JavaBeans 內省 API |
| java.beans.BeanInfo | JavaBeans 元信息 API |
| java.beans.BeanDescriptor JavaBeans | 信息描述符 |
| java.beans.PropertyDescriptor | JavaBeans 屬性描述符 |
| java.beans.MethodDescriptor | JavaBeans 方法描述符 |
| java.beans.EventSetDescriptor | JavaBeans 事件集合描述符 |
DataBinder 數據校驗
DataBinder與BeanWrapperbind方法生成BeanPropertyBindingResultBeanPropertyBindingResult關聯BeanWrapper
面試題
Spring 數據綁定 API 是什么?
org.springframework.validation.DataBinder
BeanWrapper 與 JavaBeans 之間關系是?
Spring 底層 JavaBeans 基礎設施的中心化接口
第十五章:Spring 類型轉換
Spring 類型轉換的實現
- 基於 JavaBeans 接口的類型轉換實現
- 基於
java.beans.PropertyEditor接口擴展
- 基於
- Spring 3.0+ 通用類型轉換實現
org.springframework.core.convert.converter.Converterorg.springframework.core.convert.converter.ConverterFactoryorg.springframework.core.convert.ConversionService
使用場景
| 場景 | 基於 JavaBeans 接口的類型轉換實現 | Spring 3.0+ 通用類型轉換實現 |
|---|---|---|
| 數據綁定 | YES | YES |
| BeanWrapper | YES | YES |
| Bean 屬性類型裝換 | YES | YES |
| 外部化屬性類型轉換 | NO | YES |
基於 JavaBeans 接口的類型轉換
- 核心職責
- 將 String 類型的內容轉化為目標類型的對象
- 擴展原理
- Spring 框架將文本內容傳遞到
PropertyEditor實現的setAsText(String)方法 PropertyEditor#setAsText(String)方法實現將 String 類型轉化為目標類型的對象- 將目標類型的對象傳入
PropertyEditor#setValue(Object)方法 PropertyEditor#setValue(Object)方法實現需要臨時存儲傳入對象- Spring 框架將通過
PropertyEditor#getValue()獲取類型轉換后的對象
- Spring 框架將文本內容傳遞到
Spring 內建 PropertyEditor 擴展
內建擴展(org.springframework.beans.propertyeditors 包下)
| 轉換場景 | 實現類 |
|---|---|
| String -> Byte 數組 | org.springframework.beans.propertyeditors.ByteArrayPropertyEditor |
| String -> Char | org.springframework.beans.propertyeditors.CharacterEditor |
| String -> Char 數組 | org.springframework.beans.propertyeditors.CharArrayPropertyEditor |
| String -> Charset | org.springframework.beans.propertyeditors.CharsetEditor |
| String -> Class | org.springframework.beans.propertyeditors.ClassEditor |
| String -> Currency | org.springframework.beans.propertyeditors.CurrencyEditor |
自定義 PropertyEditor 擴展
- 擴展模式
- 擴展
java.beans.PropertyEditorSupport類
- 擴展
- 實現
org.springframework.beans.PropertyEditorRegistrar- 實現
registerCustomEditors(org.springframework.beans.PropertyEditorRegistry)方法 - 將
PropertyEditorRegistrar實現注冊為 Spring Bean - 聲明
org.springframework.beans.factory.config.CustomEditorConfigurer,並將自定義PropertyEditorRegistrar加入屬性propertyEditorRegistrars
- 實現
- 向
org.springframework.beans.PropertyEditorRegistry注冊自定義PropertyEditor實現- 通用類型實現
registerCustomEditor(Class<?>, PropertyEditor) - Java Bean 屬性類型實現:
registerCustomEditor(Class<?>, String, PropertyEditor)
- 通用類型實現
注意兩個接口的不同:
org.springframework.beans.PropertyEditorRegistrarorg.springframework.beans.PropertyEditorRegistry
相關的兩個類:
java.beans.PropertyEditorjava.beans.PropertyEditorSupport
Spring PropertyEditor 的設計缺陷
- 違反職責單一原則
java.beans.PropertyEditor接口職責太多,除了類型轉換,還包括 Java Beans 事件和 Java GUI 交互
java.beans.PropertyEditor實現類型局限- 來源類型只能為
java.lang.String類型
- 來源類型只能為
java.beans.PropertyEditor實現缺少類型安全- 除了實現類命名可以表達語義,實現類無法感知目標轉換類型
Spring 3 通用類型轉換接口
- 類型轉換接口 -
org.springframework.core.convert.converter.Converter<S,T>- 泛型參數 S:來源類型,參數 T:目標類型
- 核心方法:T convert(S)
- 通用類型轉換接口 -
org.springframework.core.convert.converter.GenericConverter- 核心方法:
convert(Object,TypeDescriptor,TypeDescriptor) - 配對類型:
org.springframework.core.convert.converter.GenericConverter.ConvertiblePair - 類型描述:
org.springframework.core.convert.TypeDescriptor
- 核心方法:
Spring 內建類型轉換器
| 轉換場景 | 實現類所在包名(package) |
|---|---|
| 日期/時間相關 | org.springframework.format.datetime |
| Java 8 日期/時間相關 | org.springframework.format.datetime.standard |
| 通用實現 | org.springframework.core.convert.support |
Converter 接口的局限性
- 局限一:缺少 Source Type 和 Target Type 前置判斷
- 應對:增加
org.springframework.core.convert.converter.ConditionalConverter實現- 判斷泛型相關的類
org.springframework.core.convert.TypeDescriptororg.springframework.core.ResolvableType
- 判斷泛型相關的類
- 應對:增加
- 局限二:僅能轉換單一的 Source Type 和 Target Type,不支持復合類型(比如 Collection、Map、數組等)
- 應對:使用
org.springframework.core.convert.converter.GenericConverter代替
- 應對:使用
GenericConverter 接口
org.springframework.core.convert.converter.GenericConverter
| 核心要素 | 說明 |
|---|---|
| 使用場景 | 用於“復合”類型轉換場景,比如 Collection、Map、數組等 |
| 轉換范圍 | Set<ConvertiblePair> getConvertibleTypes() |
| 配對類型 | org.springframework.core.convert.converter.GenericConverter.ConvertiblePair |
| 轉換方法 | convert(Object,TypeDescriptor,TypeDescriptor) |
| 類型描述 | org.springframework.core.convert.TypeDescriptor |
GenericConverter 接口處理復合類型,Converter 接口處理簡單類型,兩者可以相互配合
優化 GenericConverter 接口
GenericConverter局限性- 缺少 Source Type 和 Target Type 前置判斷
- 單一類型轉換實現復雜
- GenericConverter 優化接口 -
ConditionalGenericConverter- 復合類型轉換:
org.springframework.core.convert.converter.GenericConverter - 類型條件判斷:
org.springframework.core.convert.converter.ConditionalConverter
- 復合類型轉換:
擴展 Spring 類型轉換器
- 實現轉換器接口
org.springframework.core.convert.converter.Converterorg.springframework.core.convert.converter.ConverterFactoryorg.springframework.core.convert.converter.GenericConverter
- 注冊轉換器實現
- 通過
ConversionServiceFactoryBeanSpring Bean- 聲明名稱為
conversionService的ConversionServiceFactoryBean - 將自定義
Converter傳入屬性converters
- 聲明名稱為
- 通過
org.springframework.core.convert.ConversionServiceAPI
- 通過
統一類型轉換服務
org.springframework.core.convert.ConversionService
| 實現類型 | 說明 |
|---|---|
GenericConversionService |
通用 ConversionService 模板實現,不內置轉化器實現 |
DefaultConversionService |
基礎 ConversionService 實現,內置常用轉化器實現 |
FormattingConversionService |
通用 Formatter + GenericConversionService 實現,不內置轉化器和 Formatter 實現 |
DefaultFormattingConversionService |
DefaultConversionService + 格式化 實現(如:JSR-354 Money & Currency, JSR-310 Date-Time) |
ConversionService 作為依賴
-
類型轉換器底層接口 -
org.springframework.beans.TypeConverter- 起始版本:Spring 2.0
- 核心方法 -
convertIfNecessary重載方法 - 抽象實現 -
org.springframework.beans.TypeConverterSupport - 簡單實現 -
org.springframework.beans.SimpleTypeConverter
-
類型轉換器底層抽象實現 -
org.springframework.beans.TypeConverterSupport- 實現接口 -
org.springframework.beans.TypeConverter - 擴展實現 -
org.springframework.beans.PropertyEditorRegistrySupport - 委派實現 -
org.springframework.beans.TypeConverterDelegate
- 實現接口 -
-
類型轉換器底層委派實現 -
org.springframework.beans.TypeConverterDelegate- 構造來源 -
org.springframework.beans.AbstractNestablePropertyAccessor實現org.springframework.beans.BeanWrapperImpl
- 依賴 -
java.beans.PropertyEditor實現- 默認內建實現 -
PropertyEditorRegistrySupport#registerDefaultEditors
- 默認內建實現 -
- 可選依賴 -
org.springframework.core.convert.ConversionService實現
- 構造來源 -
整體流程:
// AbstractApplicationContext -> "conversionService" ConversionService Bean
// -> ConfigurableBeanFactory#setConversionService(ConversionService)
// -> AbstractAutowireCapableBeanFactory.instantiateBean
// -> AbstractBeanFactory#getConversionService ->
// BeanDefinition -> BeanWrapper -> 屬性轉換(數據來源:PropertyValues)->
// setPropertyValues(PropertyValues) -> TypeConverter#convertIfNecessnary
// TypeConverterDelegate#convertIfNecessnary -> PropertyEditor or ConversionService
面試題
Spring 類型轉換實現有哪些?
- 基於 JavaBeans PropertyEditor 接口實現
- Spring 3.0+ 通用類型轉換實現
Spring 類型轉換器接口有哪些?
- 類型轉換接口 -
org.springframework.core.convert.converter.Converter - 通用類型轉換接口 -
org.springframework.core.convert.converter.GenericConverter - 類型條件接口 -
org.springframework.core.convert.converter.ConditionalConverter - 綜合類型轉換接口 -
org.springframework.core.convert.converter.ConditionalGenericConverter
第十六章:Spring 泛型處理
Java 泛型基礎
-
泛型類型
- 泛型類型是在類型上參數化的泛型類或接口
-
泛型使用場景
- 編譯時強類型檢查
- 避免類型強轉
- 實現通用算法
-
泛型類型擦寫
- 泛型被引入到 Java 語言中,以便在編譯時提供更嚴格的類型檢查並支持泛型編程。類型擦除確保不會為參數化類型創建新類;因此,泛型不會產生運行時開銷。為了實現泛型,編譯器將類型擦除應用於:
- 將泛型類型中的所有類型參數替換為其邊界,如果類型參數是無邊界的,則將其替換為
Object。因此,生成的字節碼只包含普通類、接口和方法。 - 必要時插入類型轉換以保持類型安全。
- 生成橋方法以保留擴展泛型類型中的多態性。
- 將泛型類型中的所有類型參數替換為其邊界,如果類型參數是無邊界的,則將其替換為
- 泛型被引入到 Java 語言中,以便在編譯時提供更嚴格的類型檢查並支持泛型編程。類型擦除確保不會為參數化類型創建新類;因此,泛型不會產生運行時開銷。為了實現泛型,編譯器將類型擦除應用於:
Java 5 類型接口
Java 5 類型接口 - java.lang.reflect.Type
| 派生類或接口 | 說明 |
|---|---|
java.lang.Class |
Java 類 API,如 java.lang.String |
java.lang.reflect.GenericArrayType |
泛型數組類型 |
java.lang.reflect.ParameterizedType |
泛型參數類型 |
java.lang.reflect.TypeVariable |
泛型類型變量,如 Collection<E> 中的 E |
java.lang.reflect.WildcardType |
泛型通配類型 |
Java 泛型反射 API
| 類型 | API |
|---|---|
| 泛型信息(Generics Info) | java.lang.Class#getGenericInfo() |
| 泛型參數(Parameters) | java.lang.reflect.ParameterizedType |
| 泛型父類(Super Classes) | java.lang.Class#getGenericSuperclass() |
| 泛型接口(Interfaces) | java.lang.Class#getGenericInterfaces() |
| 泛型聲明(Generics Declaration) | java.lang.reflect.GenericDeclaration |
Spring 泛型類型輔助類
核心 API - org.springframework.core.GenericTypeResolver
- 版本支持:[2.5.2 , )
- 處理類型相關(Type)相關方法
resolveReturnType- 返回給定方法的返回類型
resolveType
- 處理泛型參數類型(ParameterizedType)相關方法
resolveReturnTypeArgument- 返回給定方法返回類型的泛型參數類型
resolveTypeArgumentresolveTypeArguments
- 處理泛型類型變量(TypeVariable)相關方法
getTypeVariableMap
Spring 泛型集合類型輔助類
核心 API - org.springframework.core.GenericCollectionTypeResolver
- 版本支持:[2.0 , 4.3]
- 替換實現:
org.springframework.core.ResolvableType - 處理 Collection 相關
getCollection*Type
- 處理 Map 相關
getMapKey*TypegetMapValue*Type
Spring 方法參數封裝
核心 API - org.springframework.core.MethodParameter
- 起始版本:[2.0 , )
- 元信息
- 關聯的方法 -
Method - 關聯的構造器 -
Constructor - 構造器或方法參數索引 -
parameterIndex - 構造器或方法參數類型 -
parameterType - 構造器或方法參數泛型類型 -
genericParameterType - 構造器或方法參數參數名稱 -
parameterName - 所在的類 -
containingClass
- 關聯的方法 -
Method 和 Constructor 二選一
MethodParameter 可以表示方法參數、構造器參數、返回類型
Spring 4.0 泛型優化實現 - ResolvableType
核心 API - org.springframework.core.ResolvableType
- 起始版本:[4.0 , )
- 扮演角色:
GenericTypeResolver和GenericCollectionTypeResolver替代者 - 工廠方法:
for*方法 - 轉換方法:
as*方法 - 處理方法:
resolve*方法
ResolvableType 的局限性
- 局限一:
ResolvableType無法處理泛型擦寫 - 局限二:
ResolvableType無法處理非具體化的ParameterizedType
面試題
Java 泛型擦寫發生在編譯時還是運行時?
運行時
請介紹 Java 5 Type 類型的派生類或接口?
java.lang.Classjava.lang.reflect.GenericArrayTypejava.lang.reflect.ParameterizedTypejava.lang.reflect.TypeVariablejava.lang.reflect.WildcardType
請說明 ResolvableType 的設計優勢?
- 簡化 Java 5 Type API 開發,屏蔽復雜 API 的運用,如
ParameterizedType - 不變性設計(Immutability)
- Fluent API 設計(Builder 模式),鏈式(流式)編程
第十七章:Spring事件
Java事件/監聽器編程模型
- 設計模式-觀察者模式擴展
- 可觀者對象(消息發送者)-
java.util.Observable - 觀察者 -
java.util.Observer
- 可觀者對象(消息發送者)-
- 標准化接口
- 事件對象 -
java.util.EventObject - 事件監聽器 -
java.util.EventListener
- 事件對象 -
面向接口的事件/監聽器設計模式
事件/監聽器場景舉例
| Java 技術規范 | 事件接口 | 監聽器接口 |
|---|---|---|
| JavaBeans | java.beans.PropertyChangeEvent |
java.beans.PropertyChangeListener |
| Java AWT | java.awt.event.MouseEvent |
java.awt.event.MouseListener |
| Java Swing | javax.swing.event.MenuEvent |
javax.swing.event.MenuListener |
| Java Preference | java.util.prefs.PreferenceChangeEvent |
java.util.prefs.PreferenceChangeListener |
面向注解的事件/監聽器設計模式
事件/監聽器注解場景舉例
| Java 技術規范 | 事件注解 | 監聽器注解 |
|---|---|---|
| Servlet 3.0+ | @javax.servlet.annotation.WebListener |
|
| JPA 1.0+ | @javax.persistence.PostPersist |
|
| Java Common | @PostConstruct |
|
| EJB 3.0+ | @javax.ejb.PrePassivate |
|
| JSF 2.0+ | @javax.faces.event.ListenerFor |
Spring 標准事件 - ApplicationEvent
org.springframework.context.ApplicationEvent- Java 標准事件
java.util.EventObject擴展- 擴展特性:事件發生時間戳
- Spring 應用上下文
ApplicationEvent擴展 -ApplicationContextEventorg.springframework.context.event.ApplicationContextEvent- Spring 應用上下文(ApplicationContext)作為事件源
- 具體實現:
org.springframework.context.event.ContextClosedEventorg.springframework.context.event.ContextRefreshedEventorg.springframework.context.event.ContextStartedEventorg.springframework.context.event.ContextStoppedEvent

基於接口的 Spring 事件監聽器
Java 標准事件監聽器 java.util.EventListener 擴展
- 擴展接口 -
org.springframework.context.ApplicationListener - 設計特點:單一類型事件處理
- 處理方法:
onApplicationEvent(ApplicationEvent) - 事件類型:
org.springframework.context.ApplicationEvent
基於注解的 Spring 事件監聽器
Spring 注解 - @org.springframework.context.event.EventListener
| 特性 | 說明 |
|---|---|
| 設計特點 | 支持多 ApplicationEvent 類型,無需接口約束 |
| 注解目標 | 方法 |
| 是否支持異步執行 | 支持 |
| 是否支持泛型類型事件 | 支持 |
| 是指支持順序控制 | 支持,配合 @Order 注解控制 |
注冊 Spring ApplicationListener
- 基於 Spring 接口:向 Spring 應用上下文注冊事件
ApplicationListener作為 Spring Bean 注冊- 通過
ConfigurableApplicationContext#addApplicationListenerAPI 注冊
- 基於 Spring 注解:
@org.springframework.context.event.EventListener
Spring事件發布器
- 方法一:通過
ApplicationEventPublisher發布 Spring 事件- 獲取 ApplicationEventPublisher
- 依賴注入
- 獲取 ApplicationEventPublisher
- 方法二:通過
ApplicationEventMulticaster發布 Spring 事件- 獲取
ApplicationEventMulticaster- 依賴注入
- 依賴查找
- 獲取
Spring層次性上下文事件傳播
- 發生說明
- 當 Spring 應用出現多層次 Spring 應用上下文(ApplicationContext)時,如 Spring WebMVC、Spring Boot 或 Spring Cloud 場景下,由子
ApplicationContext發起 Spring 事件可能會傳遞到其 ParentApplicationContext(直到 Root)的過程
- 當 Spring 應用出現多層次 Spring 應用上下文(ApplicationContext)時,如 Spring WebMVC、Spring Boot 或 Spring Cloud 場景下,由子
- 如何避免
- 定位 Spring 事件源(ApplicationContext)進行過濾處理
Spring內建事件
ApplicationContextEvent 派生事件
ContextRefreshedEvent:Spring 應用上下文就緒事件ContextStartedEvent:Spring 應用上下文啟動事件ContextStoppedEvent:Spring 應用上下文停止事件ContextClosedEvent:Spring 應用上下文關閉事件
Spring 4.2 Payload 事件
Spring Payload 事件 - org.springframework.context.PayloadApplicationEvent
-
使用場景:簡化 Spring 事件發送,關注事件源主體
-
發送方法
ApplicationEventPublisher#publishEvent(java.lang.Object)
自定義 Spring 事件
- 擴展
org.springframework.context.ApplicationEvent - 實現
org.springframework.context.ApplicationListener - 將
org.springframework.context.ApplicationListener注冊到容器內
依賴注入 ApplicationEventPublisher
- 通過
ApplicationEventPublisherAware回調接口 - 通過
@Autowired ApplicationEventPublisher
依賴查找 ApplicationEventMulticaster
- 查找條件
- Bean 名稱:
applicationEventMulticaster - Bean 類型:
org.springframework.context.event.ApplicationEventMulticaster
- Bean 名稱:
AbstractApplicationContext#earlyApplicationEvents 在 prepareRefresh() 中從 null 被賦值,在 registerListeners() 的最后,重新被賦值為 null,並進行了早期事件的發布,
因為可能存在一個 Bean 同時實現 BeanPostProcessor 和 ApplicationEventPublisherAware,並在 ApplicationEventPublisherAware#setApplicationEventPublisher 方法中發送事件
因為實現了 BeanPostProcessor,所以這個 Bean 在 registerBeanPostProcessors(beanFactory); 這個方法中初始化,而此時 initApplicationEventMulticaster(); 還沒有執行,所以 AbstractApplicationContext#applicationEventMulticaster 為空,無法發布事件,所以先將事件保存在 AbstractApplicationContext#earlyApplicationEvents 中
ApplicationEventPublisher 底層實現
- 接口:
org.springframework.context.event.ApplicationEventMulticaster- 抽象類:
org.springframework.context.event.AbstractApplicationEventMulticaster- 實現類:
org.springframework.context.event.SimpleApplicationEventMulticaster
- 實現類:
- 抽象類:
同步和異步 Spring 事件廣播
- 基於實現類 -
org.springframework.context.event.SimpleApplicationEventMulticaster- 模式切換:
setTaskExecutor(java.util.concurrent.Executor)方法- 默認模式:同步
- 異步模式:如
java.util.concurrent.ThreadPoolExecutor
- 設計缺陷:不是基於接口契約編程,實現依賴於類本身
- 模式切換:
- 基於注解 -
@org.springframework.context.event.EventListener- 模式切換
- 默認模式:同步
- 異步模式:標注
@org.springframework.scheduling.annotation.Async
- 實現限制:無法直接實現同步/異步動態切換
- 模式切換
區別:
- 使用
SimpleApplicationEventMulticaster#setTaskExecutor的影響是全局的,使用@Async是局部的 - 使用
SimpleApplicationEventMulticaster#setTaskExecutor需要手動關閉線程池,使用@Async不需要
Spring 4.1 事件異常處理
- Spring 3.0 錯誤處理接口 -
org.springframework.util.ErrorHandler - 使用場景
- Spring 事件(Events)
SimpleApplicationEventMulticasterSpring 4.1 開始支持- Spring 本地調度(Scheduling)
org.springframework.scheduling.concurrent.ConcurrentTaskSchedulerorg.springframework.scheduling.concurrent.ThreadPoolTaskScheduler
Spring 事件/監聽器實現原理
- 核心類 -
org.springframework.context.event.SimpleApplicationEventMulticaster- 設計模式:觀察者模式擴展
- 被觀察者 -
org.springframework.context.ApplicationListener- API 添加
- 依賴查找
- 通知對象 -
org.springframework.context.ApplicationEvent
- 被觀察者 -
- 執行模式:同步/異步
- 異常處理:
org.springframework.util.ErrorHandler - 泛型處理:
org.springframework.core.ResolvableType
- 設計模式:觀察者模式擴展
課外資料
Spring Boot 事件
| 事件類型 | 發生時機 |
|---|---|
| ApplicationStartingEvent | 當 Spring Boot 應用已啟動時 |
| ApplicationStartedEvent | 當 Spring Boot 應用已啟動時 |
| ApplicationEnvironmentPreparedEvent | 當 Spring Boot Environment 實例已准備時 |
| ApplicationPreparedEvent | 當 Spring Boot 應用預備時 |
| ApplicationReadyEvent | 當 Spring Boot 應用完全可用時 |
| ApplicationFailedEvent | 當 Spring Boot 應用啟動失敗時 |
Spring Cloud 事件
| 事件類型 | 發生時機 |
|---|---|
| EnvironmentChangeEvent | 當 Environment 示例配置屬性發生變化時 |
| HeartbeatEvent | 當 Discoveryclient 客戶端發送心跳時 |
| InstancePreRegisteredEvent | 當服務實例注冊前 |
| InstanceRegisteredEvent | 當服務實例注冊后 |
| RefreshEvent | 當 RefreshEndpoint 被調用時 |
| RefreshScopeRefreshedEvent | 當 Refresh Scope Bean 刷新后 |
面試題
Spring 事件核心接口/組件?
- Spring 事件 -
org.springframework.context.ApplicationEvent - Spring 事件監聽器 -
org.springframework.context.ApplicationListener - Spring 事件發布器 -
org.springframework.context.ApplicationEventPublisher - Spring 事件廣播器 -
org.springframework.context.event.ApplicationEventMulticaster
Spring 同步和異步事件處理的使用場景?
- Spring 同步事件 - 絕大多數 Spring 使用場景,如
ContextRefreshedEvent - Spring 異步事件 - 主要
@EventListener與@Async配合,實現異步處理,不阻塞主線程,比如長時間的數據計算任務等。不要輕易調整SimpleApplicationEventMulticaster中關聯的taskExecutor對象,除非使用者非常了解 Spring 事件機制,否則容易出現異常行為。
第十八章:Spring 注解
Spring 注解驅動編程發展歷程
- 注解驅動啟蒙時代:Spring Framework 1.x
@Transactional@ManagedResource
- 注解驅動過渡時代:Spring Framework 2.x
@Component@Repository@Service@Controller
- 注解驅動黃金時代:Spring Framework 3.x
@Bean@Profile@Import@ImportResource@ComponentScan@Lazy@PropertySource
- 注解驅動完善時代:Spring Framework 4.x
@Conditional
- 注解驅動當下時代:Spring Framework 5.x
@Indexed
Spring 核心注解場景分類
Spring 模式注解
| Spring 注解 | 場景說明 | 起始版本 |
|---|---|---|
| @Repository | 數據倉儲模式注解 | 2.0 |
| @Component | 通用組件模式注解 | 2.5 |
| @Service | 服務模式注解 | 2.5 |
| @Controller | Web 控制器模式注解 | 2.5 |
| @Configuration | 配置類模式注解 | 3.0 |
裝配注解
| Spring 注解 | 場景說明 | 起始版本 |
|---|---|---|
| @lmportResource | 替換 XML 元素 <import> |
2.5 |
| @lmport | 導入 Configuration 類 | 2.5 |
| @ComponentScan | 掃描指定 package 下標注 Spring 模式注解的類 | 3.1 |
依賴注入注解
| Spring 注解 | 場景說明 | 起始版本 |
|---|---|---|
| @Autowired | Bean 依賴注入,支持多種依賴查找方式 | 2.5 |
| @Qualifier | 細粒度的 @Autowired 依賴查找 | 2.5 |
Spring 注解編程模型
- 編程模型
- 元注解(Meta-Annotations)
- Spring 模式注解(Stereotype Annotations)
- Spring 組合注解(Composed Annotations)
- Spring 注解屬性別名和覆蓋(Attribute Aliases and Overrides)
Spring 元注解(Meta-Annotations)
java.lang.annotation.Documentedjava.lang.annotation.Inheritedjava.lang.annotation.Repeatable
Spring 模式注解(Stereotype An notations)
-
理解
@Component"派生性"- 元標注
@Component的注解在 XML 元素<context:component-scan>或注解@ComponentScan掃描中 “派生” 了@Component的特性,並且從 Spring Framework 4.0 開始支持多層次 ”派生性“。
- 元標注
-
舉例說明
@Repository@Service@Controller@Configuration@SpringBootConfiguration(Spring Boot)
-
@Component"派生性" 原理- 核心組件 -
org.springframework.context.annotation.ClassPathBeanDefinitionScannerorg.springframework.context.annotation.ClassPathScanningCandidateComponentProvider
- 資源處理 -
org.springframework.core.io.support.ResourcePatternResolver - 資源 - 類元信息
org.springframework.core.type.classreading.MetadataReaderFactory
- 類元信息 -
org.springframework.core.type.ClassMetadata- ASM 實現 -
org.springframework.core.type.classreading.ClassMetadataReadingVisitor - 反射實現 -
org.springframework.core.type.StandardAnnotationMetadata
- ASM 實現 -
- 注解元信息 -
org.springframework.core.type.AnnotationMetadata- ASM 實現 -
org.springframework.core.type.classreading.AnnotationMetadataReadingVisitor - 反射實現 -
org.springframework.core.type.StandardAnnotationMetadata
- ASM 實現 -
- 核心組件 -
-
org.springframework.context.annotation.ComponentScanAnnotationParser
Spring 組合注解(Composed Annotations)
- 基本定義
- Spring 組合注解(Composed Annotations)中的元注解允許是 Spring 模式注解(Stereotype Annotation)與 其他Spring功能性注解的任意組合。
相關類:
org.springframework.context.annotation.ConfigurationClassParserorg.springframework.core.annotation.AnnotationAttributes
Spring 注解屬性別名(Attribute Aliases)
- 顯性別名:一個注解類中兩個屬性通過
@AliasFor互相聲明別名org.springframework.context.annotation.ComponentScan的value和basePackages
- 隱形別名:通過在注解屬性上
@AliasFor元注解的屬性org.springframework.boot.autoconfigure.SpringBootApplication的exclude、excludeName
- 傳遞隱式別名:給定一個批注中的兩個或更多屬性,它們通過
@AliasFor聲明為元注解中的屬性的顯式替代,如果這些屬性有效地替代了元注解中的同一屬性,則它們是可傳遞的隱式別名。
Spring 注解屬性覆蓋(Attribute Overrides)
- 隱式覆蓋:注解中存在和元注解中名稱相同的屬性
- 顯式覆蓋:不同名稱屬性,使用
@AliasFor聲明為元注解屬性的別名 - 傳遞顯式覆蓋:
Spring @Enable 模塊驅動
-
@Enable 模塊驅動
- @Enable 模塊驅動是以
@Enable為前綴的注解驅動編程模型。所謂 "模塊" 是指具備相同領域的功能組件集合,組合所形成一個獨立的單元。比如 WebMVC 模塊、AspectJ 代理模塊、Caching (緩存)模塊、JMX (Java 管理擴展)模塊、Async (異步處理)模塊等。
- @Enable 模塊驅動是以
-
舉例說明
@EnableWebMvc@EnableTransactionManagement@EnableCaching@EnableMBeanExport@EnableAsync
-
@Enable 模塊驅動編程模式
- 驅動注解:@EnableXXX
- 導入注解:
@lmport具體實現 - 具體實現
- 基於 Configuration Class
- 基於
ImportSelector接口實現 - 基於
ImportBeanDefinitionRegistrar接口實現
Spring條件注解
-
基於配置條件注解 -
@org.springframework.context.annotation.Profile- 關聯對象 -
org.springframework.core.env.Environment中的 Profiles - 實現變化:從 Spring 4.0 開始,
@Profile基於@Conditional實現
- 關聯對象 -
-
基於編程條件注解 -
@org.springframework.context.annotation.Conditional- 關聯對象 -
org.springframework.context.annotation.Condition具體實現
- 關聯對象 -
-
@Conditional實現原理- 上下文對象 -
org.springframework.context.annotation.ConditionContext - 條件判斷 -
org.springframework.context.annotation.ConditionEvaluator - 配置階段 -
org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase - 判斷入口 -
org.springframework.context.annotation.ConfigurationClassPostProcessororg.springframework.context.annotation.ConfigurationClassParser
- 上下文對象 -
@Conditional 屬性可傳入多個 Condition 類作為條件,這些條件按照 Order 排序,如果一個條件通過,就返回 true
課外資料
- Spring Boot 注解
| 注解 | 場景說明 | 起始版本 |
|---|---|---|
@SpringBootConfiguration |
Spring Boot 配置類 | 1.4.0 |
@SpringBootApplication |
Spring Boot 應用引導注解 | 1.2.0 |
@EnableAutoConfiguration |
Spring Boot 激活自動裝配 | 1.0.0 |
- Spring Cloud 注解
| 注解 | 場景說明 | 起始版本 |
|---|---|---|
@SpringCloudApplication |
Spring Cloud 應用引導注解 | 1.0.0 |
@EnableDiscoveryClient |
Spring Cloud 激活服務發現客戶端注解 | 1.0.0 |
@EnableCircuitBreaker |
Spring Cloud 激活熔斷注解 | 1.0.0 |
面試題
Spring模式注解有哪些?
@org.springframework.stereotype.Component@org.springframework.stereotype.Repository@org.springframework.stereotype.Service@org.springframework.stereotype.Controller@org.springframework.context.annotation.Configuration
@EventListener 的工作原理?
源碼導讀 - org.springframework.context.event.EventListenerMethodProcessor
