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 的工作原理?

posted @ 2020-11-08 21:44  流星<。)#)))≦  阅读(1195)  评论(0编辑  收藏  举报