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