20201108 小馬哥講Spring核心編程思想 - 筆記 12-18


第十二章: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

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:消息格式類型,可選項,每種類型在 numberdatetimechoice 類型選其一
    • FormatStyle:消息格式風格,可選項,包括:shortmediumlongfullintegercurrencypercent

高級特性

  • 重置消息格式模式
  • 重置 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

Errors 文案來源

Errors 文案生成步驟

  • 選擇 Errors 實現(如:org.springframework.validation.BeanPropertyBindingResult
  • 調用 rejectrejectValue 方法
  • 獲取 Errors 對象中 ObjectErrorFieldError
  • ObjectErrorFieldError 中的 codeargs,關聯 MessageSource 實現(如:ResourceBundleMessageSource

Errors 不能直接生成文案,但是可以提供國際化接口 MessageSource 所需要的 codeargs

自定義 Validator

實現 org.springframework.validation.Validator 接口

  • 實現 supports 方法
  • 實現 validate 方法
    • 通過 Errors 對象收集錯誤
      • ObjectError:對象(Bean)錯誤:
      • FieldError:對象(Bean)屬性(Property)錯誤
    • 通過 ObjectErrorFieldError 關聯 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 基礎設施的中心化接口
  • 通常不會直接使用,間接用於 BeanFactoryDataBinder
  • 提供標准 JavaBeans 分析和操作,能夠單獨或批量存儲 Java Bean 的屬性(properties)
  • 支持嵌套屬性路徑(nested path)
  • 實現類 org.springframework.beans.BeanWrapperImpl

Spring 底層 Java Beans 替換實現

  • JavaBeans 核心實現 - java.beans.BeanInfo
    • 屬性(Property)
      • java.beans.PropertyEditor
    • 方法(Method)
    • 事件(Event)
    • 表達式(Expression)
  • Spring 替代實現 - org.springframework.beans.BeanWrapper
    • 屬性(Property)
      • java.beans.PropertyEditor
    • 嵌套屬性路徑(nested path)

課外資料

標准 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 數據校驗

  • DataBinderBeanWrapper
    • 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 類型的內容轉化為目標類型的對象
  • 擴展原理
    1. Spring 框架將文本內容傳遞到 PropertyEditor 實現的 setAsText(String) 方法
    2. PropertyEditor#setAsText(String) 方法實現將 String 類型轉化為目標類型的對象
    3. 將目標類型的對象傳入 PropertyEditor#setValue(Object) 方法
    4. PropertyEditor#setValue(Object) 方法實現需要臨時存儲傳入對象
    5. Spring 框架將通過 PropertyEditor#getValue() 獲取類型轉換后的對象

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 擴展

  1. 擴展模式
    • 擴展 java.beans.PropertyEditorSupport
  2. 實現 org.springframework.beans.PropertyEditorRegistrar
    • 實現 registerCustomEditors(org.springframework.beans.PropertyEditorRegistry) 方法
    • PropertyEditorRegistrar 實現注冊為 Spring Bean
    • 聲明 org.springframework.beans.factory.config.CustomEditorConfigurer,並將自定義 PropertyEditorRegistrar 加入屬性 propertyEditorRegistrars
  3. 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
      • 聲明名稱為 conversionServiceConversionServiceFactoryBean
      • 將自定義 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 類型轉換實現有哪些?

  1. 基於 JavaBeans PropertyEditor 接口實現
  2. 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 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

MethodConstructor 二選一

MethodParameter 可以表示方法參數、構造器參數、返回類型

Spring 4.0 泛型優化實現 - ResolvableType

核心 API - org.springframework.core.ResolvableType

  • 起始版本:[4.0 , )
  • 扮演角色:GenericTypeResolverGenericCollectionTypeResolver 替代者
  • 工廠方法: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

img

基於接口的 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
      • 依賴注入
  • 方法二:通過 ApplicationEventMulticaster 發布 Spring 事件
    • 獲取 ApplicationEventMulticaster
      • 依賴注入
      • 依賴查找

Spring層次性上下文事件傳播

  • 發生說明
    • 當 Spring 應用出現多層次 Spring 應用上下文(ApplicationContext)時,如 Spring WebMVC、Spring Boot 或 Spring Cloud 場景下,由子 ApplicationContext 發起 Spring 事件可能會傳遞到其 Parent ApplicationContext(直到 Root)的過程
  • 如何避免
    • 定位 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 事件

  1. 擴展 org.springframework.context.ApplicationEvent
  2. 實現 org.springframework.context.ApplicationListener
  3. org.springframework.context.ApplicationListener 注冊到容器內

依賴注入 ApplicationEventPublisher

  • 通過 ApplicationEventPublisherAware 回調接口
  • 通過 @Autowired ApplicationEventPublisher

依賴查找 ApplicationEventMulticaster

  • 查找條件
    • Bean 名稱:applicationEventMulticaster
    • Bean 類型:org.springframework.context.event.ApplicationEventMulticaster
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)

Wiki

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
    • 注解元信息 - org.springframework.core.type.AnnotationMetadata
      • ASM 實現 - org.springframework.core.type.classreading.AnnotationMetadataReadingVisitor
      • 反射實現 - org.springframework.core.type.StandardAnnotationMetadata
  • 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.ComponentScanvaluebasePackages
  • 隱形別名:通過在注解屬性上 @AliasFor 元注解的屬性
    • org.springframework.boot.autoconfigure.SpringBootApplicationexcludeexcludeName
  • 傳遞隱式別名:給定一個批注中的兩個或更多屬性,它們通過 @AliasFor 聲明為元注解中的屬性的顯式替代,如果這些屬性有效地替代了元注解中的同一屬性,則它們是可傳遞的隱式別名。

Spring 注解屬性覆蓋(Attribute Overrides)

  • 隱式覆蓋:注解中存在和元注解中名稱相同的屬性
  • 顯式覆蓋:不同名稱屬性,使用 @AliasFor 聲明為元注解屬性的別名
  • 傳遞顯式覆蓋:

Spring @Enable 模塊驅動

  • @Enable 模塊驅動

    • @Enable 模塊驅動是以 @Enable 為前綴的注解驅動編程模型。所謂 "模塊" 是指具備相同領域的功能組件集合,組合所形成一個獨立的單元。比如 WebMVC 模塊、AspectJ 代理模塊、Caching (緩存)模塊、JMX (Java 管理擴展)模塊、Async (異步處理)模塊等。
  • 舉例說明

    • @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

@PropertySource 的工作原理?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM