第十二章:Spring 國際化
Spring 國際化使用場景
- 普通國際化文案
- Bean Validation 校驗國際化文案
- Web 站點頁面渲染
- Web MVC 錯誤消息提示
Spring 國際化接口
- 核心接口 -
org.springframework.context.MessageSource
- 主要概念
- 文案模板編碼(code)
- 文案模板參數(args)
- 區域(Locale)
層次性 MessageSource
- Spring 層次性接口回顧
org.springframework.beans.factory.HierarchicalBeanFactory
org.springframework.context.ApplicationContext
org.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 通過外部化配置簡化
MessageSource
Bean 構建 - Spring Boot 基於 Bean Validation 校驗非常普遍
SpringBoot 中關於 MessageSource
的自動配置類
org.springframework.context.support.ResourceBundleMessageSource
面試題
Spring 國際化接口有哪些?
- 核心接口 -
MessageSource
- 層次性接口 -
org.springframework.context.HierarchicalMessageSource
Spring 有哪些 MessageSource 內建實現?
org.springframework.context.support.ResourceBundleMessageSource
org.springframework.context.support.ReloadableResourceBundleMessageSource
org.springframework.context.support.StaticMessageSource
org.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.Validated
javax.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.WebDataBinder
org.springframework.web.bind.ServletRequestDataBinder
org.springframework.web.bind.support.WebRequestDataBinder
org.springframework.web.bind.support.WebExchangeDataBinder
(since 5.0)
DataBinder 核心屬性
屬性 | 說明 |
---|---|
target | 關聯目標 Bean |
objectName | 目標 Bean名稱 |
bindingResult | 屬性綁定結果 |
typeConverter | 類型轉換器 |
conversionService | 類型轉換服務 |
messageCodesResolver | 校驗錯誤文案 Code 處理器 |
validators | 關聯的 Bean Validator 實例集合 |
DataBinder 綁定方法
bind(PropertyValues)
:將PropertyValues
Key-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
與BeanWrapper
bind
方法生成BeanPropertyBindingResult
BeanPropertyBindingResult
關聯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.Converter
org.springframework.core.convert.converter.ConverterFactory
org.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.PropertyEditorRegistrar
org.springframework.beans.PropertyEditorRegistry
相關的兩個類:
java.beans.PropertyEditor
java.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.TypeDescriptor
org.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.Converter
org.springframework.core.convert.converter.ConverterFactory
org.springframework.core.convert.converter.GenericConverter
- 注冊轉換器實現
- 通過
ConversionServiceFactoryBean
Spring Bean- 聲明名稱為
conversionService
的ConversionServiceFactoryBean
- 將自定義
Converter
傳入屬性converters
- 聲明名稱為
- 通過
org.springframework.core.convert.ConversionService
API
- 通過
統一類型轉換服務
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
- 返回給定方法返回類型的泛型參數類型
resolveTypeArgument
resolveTypeArguments
- 處理泛型類型變量(TypeVariable)相關方法
getTypeVariableMap
Spring 泛型集合類型輔助類
核心 API - org.springframework.core.GenericCollectionTypeResolver
- 版本支持:[2.0 , 4.3]
- 替換實現:
org.springframework.core.ResolvableType
- 處理 Collection 相關
getCollection*Type
- 處理 Map 相關
getMapKey*Type
getMapValue*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.Class
java.lang.reflect.GenericArrayType
java.lang.reflect.ParameterizedType
java.lang.reflect.TypeVariable
java.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
擴展 -ApplicationContextEvent
org.springframework.context.event.ApplicationContextEvent
- Spring 應用上下文(ApplicationContext)作為事件源
- 具體實現:
org.springframework.context.event.ContextClosedEvent
org.springframework.context.event.ContextRefreshedEvent
org.springframework.context.event.ContextStartedEvent
org.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#addApplicationListener
API 注冊
- 基於 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)
SimpleApplicationEventMulticaster
Spring 4.1 開始支持- Spring 本地調度(Scheduling)
org.springframework.scheduling.concurrent.ConcurrentTaskScheduler
org.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.Documented
java.lang.annotation.Inherited
java.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.ClassPathBeanDefinitionScanner
org.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.ConfigurationClassParser
org.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.ConfigurationClassPostProcessor
org.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