From 8d818124c1401309c6891fbccab3bee5045649fd Mon Sep 17 00:00:00 2001 From: senntyou Date: Tue, 19 Oct 2021 16:55:18 +0800 Subject: [PATCH] add spring/5 --- README.md | 1 + spring/5.md | 1167 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1168 insertions(+) create mode 100644 spring/5.md diff --git a/README.md b/README.md index 7cd3ce6..2b7da7d 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ 1. [\[2021-10-12\] Spring 源码解析二:上下文组件(WebApplicationContext)](spring/2.md) 1. [\[2021-10-14\] Spring 源码解析三:Bean 的注册、解析、实例化机制](spring/3.md) 1. [\[2021-10-14\] Spring 源码解析四:Bean 的构造装载、属性值解析、注解扫描](spring/4.md) +1. [\[2021-10-19\] Spring 源码解析五:Bean 的配置、定义、注册](spring/5.md) ## 前端进阶 diff --git a/spring/5.md b/spring/5.md new file mode 100644 index 0000000..a1cb849 --- /dev/null +++ b/spring/5.md @@ -0,0 +1,1167 @@ +# Spring 源码解析五:Bean 的配置、定义、注册 + +在 [Spring 源码解析二:上下文组件(WebApplicationContext)](./2.md) 中,留有一些点待解析: + +- `ConfigurableListableBeanFactory`如何加载、实例化 bean +- `ResourceEditorRegistrar`如何注册属性编辑器、属性编辑器如何解析为对象 +- `PathMatchingResourcePatternResolver`如何解析、加载 locationPattern 指定的资源 +- `PropertySourcesPropertyResolver`如何是解析路径的 +- `XmlBeanDefinitionReader`如何是解析 bean 定义的 +- `AnnotatedBeanDefinitionReader`是如何注册 bean 的 +- `ClassPathBeanDefinitionScanner`是如何扫描包的 + +其中第一条已在 [Spring 源码解析三:Bean 的注册、解析、实例化机制](./3.md) 中解析了,这一节来看看后面几条 + +## 1. ResourceEditorRegistrar + +[ResourceEditorRegistrar](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-beans/src/main/java/org/springframework/beans/support/ResourceEditorRegistrar.java) +的主要功能是在 bean 实例化的时候,在 beanDefinition 转换为 BeanWrapper 后用于对属性的填充 + +比如说,xml 中这样定义了一个 bean + +```xml + + + +``` + +在`com.example.DemoBean`类中定义了一个属性`date`是`Date`类型的,但 Spring 从 xml 中读出来的是字符串,这就需要给 Spring 配置属性编辑器,把字符串转化成对象。 + +```java +public class ResourceEditorRegistrar implements PropertyEditorRegistrar { + // 注册自定义编辑器 + @Override + public void registerCustomEditors(PropertyEditorRegistry registry) { + // 注册实体,暂时省略,后面再解析 + // 这里先解析PropertyEditorRegistry + } +} +``` + +先来看看[PropertyEditorRegistrySupport](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-beans/src/main/java/org/springframework/beans/PropertyEditorRegistrySupport.java) +是如何处理各种类型的属性编辑器的(`PropertyEditorRegistry`只是接口,`PropertyEditorRegistrySupport`是其默认实现) + +### 1.1. PropertyEditorRegistrySupport + +```java +public class PropertyEditorRegistrySupport implements PropertyEditorRegistry { + // 默认编辑器 + private Map, PropertyEditor> defaultEditors; + // 覆盖默认编辑器 + private Map, PropertyEditor> overriddenDefaultEditors; + // 自定义编辑器 + private Map, PropertyEditor> customEditors; + // Path自定义编辑器 + private Map customEditorsForPath; + // 自定义编辑器缓存 + private Map, PropertyEditor> customEditorCache; +} +``` + +#### 1.1.1. PropertyEditorRegistrySupport.getDefaultEditor + +```java +public class PropertyEditorRegistrySupport implements PropertyEditorRegistry { + // 获取针对requiredType的默认编辑器 + public PropertyEditor getDefaultEditor(Class requiredType) { + // ... 代码省略 + + if (this.overriddenDefaultEditors != null) { + // 如果有自定义覆盖默认编辑器的,返回自定义默认编辑器 + PropertyEditor editor = this.overriddenDefaultEditors.get(requiredType); + if (editor != null) { + return editor; + } + } + // 如果defaultEditors还没创建过,则创建 + if (this.defaultEditors == null) { + createDefaultEditors(); + } + return this.defaultEditors.get(requiredType); + } + + // 创建默认编辑器,所有的基本、常用类型都囊括了 + private void createDefaultEditors() { + this.defaultEditors = new HashMap<>(64); + + this.defaultEditors.put(Charset.class, new CharsetEditor()); + this.defaultEditors.put(Class.class, new ClassEditor()); + this.defaultEditors.put(Class[].class, new ClassArrayEditor()); + this.defaultEditors.put(Currency.class, new CurrencyEditor()); + this.defaultEditors.put(File.class, new FileEditor()); + this.defaultEditors.put(InputStream.class, new InputStreamEditor()); + + this.defaultEditors.put(InputSource.class, new InputSourceEditor()); + + this.defaultEditors.put(Locale.class, new LocaleEditor()); + this.defaultEditors.put(Path.class, new PathEditor()); + this.defaultEditors.put(Pattern.class, new PatternEditor()); + this.defaultEditors.put(Properties.class, new PropertiesEditor()); + this.defaultEditors.put(Reader.class, new ReaderEditor()); + this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor()); + this.defaultEditors.put(TimeZone.class, new TimeZoneEditor()); + this.defaultEditors.put(URI.class, new URIEditor()); + this.defaultEditors.put(URL.class, new URLEditor()); + this.defaultEditors.put(UUID.class, new UUIDEditor()); + this.defaultEditors.put(ZoneId.class, new ZoneIdEditor()); + + this.defaultEditors.put(Collection.class, new CustomCollectionEditor(Collection.class)); + this.defaultEditors.put(Set.class, new CustomCollectionEditor(Set.class)); + this.defaultEditors.put(SortedSet.class, new CustomCollectionEditor(SortedSet.class)); + this.defaultEditors.put(List.class, new CustomCollectionEditor(List.class)); + this.defaultEditors.put(SortedMap.class, new CustomMapEditor(SortedMap.class)); + + this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor()); + this.defaultEditors.put(char[].class, new CharArrayPropertyEditor()); + + this.defaultEditors.put(char.class, new CharacterEditor(false)); + this.defaultEditors.put(Character.class, new CharacterEditor(true)); + + this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false)); + this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true)); + + this.defaultEditors.put(byte.class, new CustomNumberEditor(Byte.class, false)); + this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true)); + this.defaultEditors.put(short.class, new CustomNumberEditor(Short.class, false)); + this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true)); + this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class, false)); + this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true)); + this.defaultEditors.put(long.class, new CustomNumberEditor(Long.class, false)); + this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true)); + this.defaultEditors.put(float.class, new CustomNumberEditor(Float.class, false)); + this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true)); + this.defaultEditors.put(double.class, new CustomNumberEditor(Double.class, false)); + this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true)); + this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, true)); + this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.class, true)); + + StringArrayPropertyEditor sae = new StringArrayPropertyEditor(); + this.defaultEditors.put(String[].class, sae); + this.defaultEditors.put(short[].class, sae); + this.defaultEditors.put(int[].class, sae); + this.defaultEditors.put(long[].class, sae); + } +} +``` + +这些默认的编辑器都比较简单,都在 [beans/propertyeditors](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-beans/src/main/java/org/springframework/beans/propertyeditors/) +这个包下,只有一个不在这个包下,也需要解析下:[ResourceArrayPropertyEditor](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-core/src/main/java/org/springframework/core/io/support/ResourceArrayPropertyEditor.java) + +`ResourceArrayPropertyEditor` 解析的是多个资源,如通配符\*代表的多个资源 + +```java +public class ResourceArrayPropertyEditor extends PropertyEditorSupport { + // 转换String值为目标值 + @Override + public void setAsText(String text) { + // 解析path中的占位符${} + String pattern = resolvePath(text).trim(); + try { + // 使用ResourcePatternResolver.getResources解析 + setValue(this.resourcePatternResolver.getResources(pattern)); + } + catch (IOException ex) { + // ... 代码省略 + } + } + + // 设置转换的值 + @Override + public void setValue(Object value) throws IllegalArgumentException { + // 如果是Collection,继续处理 + // 如果是数组,但还没有转化为Resource,继续处理 + if (value instanceof Collection || (value instanceof Object[] && !(value instanceof Resource[]))) { + Collection input = (value instanceof Collection ? (Collection) value : Arrays.asList((Object[]) value)); + Set merged = new LinkedHashSet<>(); + for (Object element : input) { + if (element instanceof String) { + // 解析path中的占位符${} + String pattern = resolvePath((String) element).trim(); + try { + // 转化为Resource,并添加 + Resource[] resources = this.resourcePatternResolver.getResources(pattern); + Collections.addAll(merged, resources); + } + catch (IOException ex) { + // ... 代码省略 + } + } + else if (element instanceof Resource) { + // 如果本来就是Resource,则添加 + merged.add((Resource) element); + } + else { + // ... 代码省略 + } + } + super.setValue(merged.toArray(new Resource[0])); + } + else { + super.setValue(value); + } + } +} +``` + +#### 1.1.2. PropertyEditorRegistrySupport.registerCustomEditor + +```java +public class PropertyEditorRegistrySupport implements PropertyEditorRegistry { + // 注册自定义编辑器 + @Override + public void registerCustomEditor(@Nullable Class requiredType, @Nullable String propertyPath, PropertyEditor propertyEditor) { + // ... 代码省略 + + // 如果有propertyPath,注册到customEditorsForPath中 + if (propertyPath != null) { + if (this.customEditorsForPath == null) { + this.customEditorsForPath = new LinkedHashMap<>(16); + } + this.customEditorsForPath.put(propertyPath, new CustomEditorHolder(propertyEditor, requiredType)); + } + // 不然,注册到customEditors中 + else { + if (this.customEditors == null) { + this.customEditors = new LinkedHashMap<>(16); + } + this.customEditors.put(requiredType, propertyEditor); + this.customEditorCache = null; + } + } + + // 添加针对requiredType的覆盖默认编辑器 + public void overrideDefaultEditor(Class requiredType, PropertyEditor propertyEditor) { + if (this.overriddenDefaultEditors == null) { + this.overriddenDefaultEditors = new HashMap<>(); + } + this.overriddenDefaultEditors.put(requiredType, propertyEditor); + } +} +``` + +### 1.2. ResourceEditorRegistrar.registerCustomEditors + +```java +public class ResourceEditorRegistrar implements PropertyEditorRegistrar { + // 注册自定义编辑器 + @Override + public void registerCustomEditors(PropertyEditorRegistry registry) { + // 使用当前的resourceLoader,覆盖Resource、InputStream、File、Path等的默认资源加载器 + // 其他的处理方式不变 + ResourceEditor baseEditor = new ResourceEditor(this.resourceLoader, this.propertyResolver); + doRegisterEditor(registry, Resource.class, baseEditor); + doRegisterEditor(registry, ContextResource.class, baseEditor); + doRegisterEditor(registry, InputStream.class, new InputStreamEditor(baseEditor)); + doRegisterEditor(registry, InputSource.class, new InputSourceEditor(baseEditor)); + doRegisterEditor(registry, File.class, new FileEditor(baseEditor)); + doRegisterEditor(registry, Path.class, new PathEditor(baseEditor)); + doRegisterEditor(registry, Reader.class, new ReaderEditor(baseEditor)); + doRegisterEditor(registry, URL.class, new URLEditor(baseEditor)); + + ClassLoader classLoader = this.resourceLoader.getClassLoader(); + doRegisterEditor(registry, URI.class, new URIEditor(classLoader)); + doRegisterEditor(registry, Class.class, new ClassEditor(classLoader)); + doRegisterEditor(registry, Class[].class, new ClassArrayEditor(classLoader)); + + if (this.resourceLoader instanceof ResourcePatternResolver) { + doRegisterEditor(registry, Resource[].class, + new ResourceArrayPropertyEditor((ResourcePatternResolver) this.resourceLoader, this.propertyResolver)); + } + } + + // 如果可以,覆盖默认编辑器,不然注册自定义编辑器 + private void doRegisterEditor(PropertyEditorRegistry registry, Class requiredType, PropertyEditor editor) { + if (registry instanceof PropertyEditorRegistrySupport) { + ((PropertyEditorRegistrySupport) registry).overrideDefaultEditor(requiredType, editor); + } + else { + registry.registerCustomEditor(requiredType, editor); + } + } +} +``` + +## 2. PathMatchingResourcePatternResolver + +[PathMatchingResourcePatternResolver](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java) +是一个 Ant 模式通配符的 Resource 查找器,可以用来查找类路径下或者文件系统中的资源 + +Ant 模式匹配规则如下 + +1. `?` 匹配一个字符 +1. `*` 匹配 0 个或多个字符 +1. `**` 匹配 0 个或多个目录 + +例如 + +- `/trip/api/*x` 匹配 `/trip/api/x`、`/trip/api/ax`、`/trip/api/abx`,但不匹配 `/trip/abc/x` +- `/trip/a/a?x` 匹配 `/trip/a/abx`,但不匹配 `/trip/a/ax`、`/trip/a/abcx` +- `/**/api/alie` 匹配 `/trip/api/alie`、`/trip/dax/api/alie`,但不匹配 `/trip/a/api` +- `/**/*.htmlm` 匹配所有以 `.htmlm` 结尾的路径 + +```java +public class PathMatchingResourcePatternResolver implements ResourcePatternResolver { + // 默认使用Ant模式通配符 + private PathMatcher pathMatcher = new AntPathMatcher(); + + // 解析 locationPattern 指定的资源 + @Override + public Resource[] getResources(String locationPattern) throws IOException { + // 以"classpath*:"开头 + if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) { + // 有*?{}符号的通配符 + if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) { + // 找出含有*?{}符号路径涵盖的资源 + return findPathMatchingResources(locationPattern); + } + else { + // 无通配符,当做纯字符对待 + return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length())); + } + } + else { + // 以"war:"开头或其他有:的路径 + int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 : + locationPattern.indexOf(':') + 1); + if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) { + // 找出含有*?{}符号路径涵盖的资源 + return findPathMatchingResources(locationPattern); + } + else { + // 不然,当做单个名字资源 + return new Resource[] {getResourceLoader().getResource(locationPattern)}; + } + } + } + +} +``` + +### 2.1. PathMatchingResourcePatternResolver.findPathMatchingResources + +```java +public class PathMatchingResourcePatternResolver implements ResourcePatternResolver { + // 找出含有*?{}符号路径涵盖的资源 + protected Resource[] findPathMatchingResources(String locationPattern) throws IOException { + // 返回根路径,如"/WEB-INF/*.xml"的根路径是"/WEB-INF/" + String rootDirPath = determineRootDir(locationPattern); + // 去除根路径的子路径 + String subPattern = locationPattern.substring(rootDirPath.length()); + // 获取根路径下的所有资源 + Resource[] rootDirResources = getResources(rootDirPath); + // 结果集 + Set result = new LinkedHashSet<>(16); + for (Resource rootDirResource : rootDirResources) { + // 解析有:的协议 + URL rootDirUrl = rootDirResource.getURL(); + // bundle协议 + if (rootDirUrl.getProtocol().startsWith("bundle")) { + // 当做UrlResource + rootDirResource = new UrlResource(rootDirUrl); + } + // vfs协议 + if (rootDirUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) { + result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirUrl, subPattern, getPathMatcher())); + } + // jar,war,zip,wsjar,vfszip协议 + else if (ResourceUtils.isJarURL(rootDirUrl) || isJarResource(rootDirResource)) { + // 从jar文件中载入资源 + result.addAll(doFindPathMatchingJarResources(rootDirResource, rootDirUrl, subPattern)); + } + else { + // 从系统文件中载入资源 + result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern)); + } + } + return result.toArray(new Resource[0]); + } +} +``` + +### 2.2. PathMatchingResourcePatternResolver.findAllClassPathResources + +```java +public class PathMatchingResourcePatternResolver implements ResourcePatternResolver { + // 找出无通配符路径涵盖的资源 + protected Resource[] findAllClassPathResources(String location) throws IOException { + // 去掉开头的/ + String path = location; + if (path.startsWith("/")) { + path = path.substring(1); + } + Set result = doFindAllClassPathResources(path); + return result.toArray(new Resource[0]); + } + + protected Set doFindAllClassPathResources(String path) throws IOException { + Set result = new LinkedHashSet<>(16); + ClassLoader cl = getClassLoader(); + // 通过classloader加载资源 + Enumeration resourceUrls = (cl != null ? cl.getResources(path) : ClassLoader.getSystemResources(path)); + while (resourceUrls.hasMoreElements()) { + URL url = resourceUrls.nextElement(); + // url转换为UrlResource,再添加 + result.add(convertClassLoaderURL(url)); + } + if (!StringUtils.hasLength(path)) { + // 处理jar资源,系统文件资源 + addAllClassLoaderJarRoots(cl, result); + } + return result; + } +} +``` + +## 3. PropertySourcesPropertyResolver + +[PropertySourcesPropertyResolver](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-core/src/main/java/org/springframework/core/env/PropertySourcesPropertyResolver.java) +的主要功能是通过`@PropertySource`注解将属性来源注入到有如`@Configuration, @Component, ...`注解的类中 + +```java +public class PropertySourcesPropertyResolver extends AbstractPropertyResolver { + // 字符值到对象的转换器,默认使用DefaultConversionService + private volatile ConfigurableConversionService conversionService; + // 占位符的前缀,默认是 ${ + private String placeholderPrefix = SystemPropertyUtils.PLACEHOLDER_PREFIX; + // 占位符的后缀,默认是 } + private String placeholderSuffix = SystemPropertyUtils.PLACEHOLDER_SUFFIX; + // 多个值的分隔符,默认 : + private String valueSeparator = SystemPropertyUtils.VALUE_SEPARATOR; +} +``` + +```java +public class PropertySourcesPropertyResolver extends AbstractPropertyResolver { + // 获取一个属性值 + protected T getProperty(String key, Class targetValueType, boolean resolveNestedPlaceholders) { + if (this.propertySources != null) { + // 从来源中遍历 + for (PropertySource propertySource : this.propertySources) { + // 来源中获取值 + Object value = propertySource.getProperty(key); + if (value != null) { + if (resolveNestedPlaceholders && value instanceof String) { + // 解析占位符 + value = resolveNestedPlaceholders((String) value); + } + + // 转换当前值到其他类型对象,如果需要的话 + return convertValueIfNecessary(value, targetValueType); + } + } + } + // 没有就返回null + return null; + } + + // 解析占位符 + protected String resolveNestedPlaceholders(String value) { + // ... 代码省略 + + return resolvePlaceholders(value); + } + + // 解析占位符 + public String resolvePlaceholders(String text) { + if (this.nonStrictHelper == null) { + this.nonStrictHelper = createPlaceholderHelper(true); + } + // 替换占位符 + return doResolvePlaceholders(text, this.nonStrictHelper); + } + + // 转换当前值到其他类型对象,如果需要的话 + protected T convertValueIfNecessary(Object value, Class targetType) { + // ... 代码省略 + + ConversionService conversionServiceToUse = this.conversionService; + if (conversionServiceToUse == null) { + // ... 代码省略 + + // 获取默认的转换器DefaultConversionService + conversionServiceToUse = DefaultConversionService.getSharedInstance(); + } + // 转换值 + return conversionServiceToUse.convert(value, targetType); + } +} +``` + +来看看 [DefaultConversionService](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-core/src/main/java/org/springframework/core/convert/support/DefaultConversionService.java) +是如何转换对象的 + +```java +public class DefaultConversionService extends GenericConversionService { + public DefaultConversionService() { + // 初始化的时候就添加默认转换器 + addDefaultConverters(this); + } + + // 添加默认转换器 + public static void addDefaultConverters(ConverterRegistry converterRegistry) { + // 添加标量类转换器 + addScalarConverters(converterRegistry); + // 添加集合类转换器 + addCollectionConverters(converterRegistry); + + // 转换ByteBuffer或字节数组到其他任意类型 + converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry)); + converterRegistry.addConverter(new StringToTimeZoneConverter()); + converterRegistry.addConverter(new ZoneIdToTimeZoneConverter()); + converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter()); + + converterRegistry.addConverter(new ObjectToObjectConverter()); + converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry)); + converterRegistry.addConverter(new FallbackObjectToStringConverter()); + converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry)); + } + + // 添加集合类转换器 + public static void addCollectionConverters(ConverterRegistry converterRegistry) { + ConversionService conversionService = (ConversionService) converterRegistry; + + // 数组转集合 + converterRegistry.addConverter(new ArrayToCollectionConverter(conversionService)); + // 集合转数组 + converterRegistry.addConverter(new CollectionToArrayConverter(conversionService)); + + // 数组转另一种数组 + converterRegistry.addConverter(new ArrayToArrayConverter(conversionService)); + // 集合转另一种集合 + converterRegistry.addConverter(new CollectionToCollectionConverter(conversionService)); + // Map转另一种Map + converterRegistry.addConverter(new MapToMapConverter(conversionService)); + + converterRegistry.addConverter(new ArrayToStringConverter(conversionService)); + converterRegistry.addConverter(new StringToArrayConverter(conversionService)); + + converterRegistry.addConverter(new ArrayToObjectConverter(conversionService)); + converterRegistry.addConverter(new ObjectToArrayConverter(conversionService)); + + converterRegistry.addConverter(new CollectionToStringConverter(conversionService)); + converterRegistry.addConverter(new StringToCollectionConverter(conversionService)); + + converterRegistry.addConverter(new CollectionToObjectConverter(conversionService)); + converterRegistry.addConverter(new ObjectToCollectionConverter(conversionService)); + + // 转换Stream到数组或集合 + converterRegistry.addConverter(new StreamConverter(conversionService)); + } + + // 添加标量类转换器 + private static void addScalarConverters(ConverterRegistry converterRegistry) { + // 一个jdk版本的数字转换到其他jdk版本的数字 + converterRegistry.addConverterFactory(new NumberToNumberConverterFactory()); + + converterRegistry.addConverterFactory(new StringToNumberConverterFactory()); + converterRegistry.addConverter(Number.class, String.class, new ObjectToStringConverter()); + + converterRegistry.addConverter(new StringToCharacterConverter()); + converterRegistry.addConverter(Character.class, String.class, new ObjectToStringConverter()); + + converterRegistry.addConverter(new NumberToCharacterConverter()); + converterRegistry.addConverterFactory(new CharacterToNumberFactory()); + + converterRegistry.addConverter(new StringToBooleanConverter()); + converterRegistry.addConverter(Boolean.class, String.class, new ObjectToStringConverter()); + + converterRegistry.addConverterFactory(new StringToEnumConverterFactory()); + converterRegistry.addConverter(new EnumToStringConverter((ConversionService) converterRegistry)); + + converterRegistry.addConverterFactory(new IntegerToEnumConverterFactory()); + converterRegistry.addConverter(new EnumToIntegerConverter((ConversionService) converterRegistry)); + + converterRegistry.addConverter(new StringToLocaleConverter()); + converterRegistry.addConverter(Locale.class, String.class, new ObjectToStringConverter()); + + converterRegistry.addConverter(new StringToCharsetConverter()); + converterRegistry.addConverter(Charset.class, String.class, new ObjectToStringConverter()); + + converterRegistry.addConverter(new StringToCurrencyConverter()); + converterRegistry.addConverter(Currency.class, String.class, new ObjectToStringConverter()); + + // String转Properties对象 + converterRegistry.addConverter(new StringToPropertiesConverter()); + // Properties对象转String + converterRegistry.addConverter(new PropertiesToStringConverter()); + + converterRegistry.addConverter(new StringToUUIDConverter()); + converterRegistry.addConverter(UUID.class, String.class, new ObjectToStringConverter()); + } +} +``` + +这些默认的编辑器都在 [convert/support](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-core/src/main/java/org/springframework/core/convert/support/) +包下,都不难,举个较复杂一点的例子 [ArrayToCollectionConverter](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-core/src/main/java/org/springframework/core/convert/support/ArrayToCollectionConverter.java) + +```java +final class ArrayToCollectionConverter implements ConditionalGenericConverter { + public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { + // ... 代码省略 + + // 先获取数组长度 + int length = Array.getLength(source); + TypeDescriptor elementDesc = targetType.getElementTypeDescriptor(); + // 创建集合 + Collection target = CollectionFactory.createCollection(targetType.getType(), + (elementDesc != null ? elementDesc.getType() : null), length); + + // 元素不需要转换 + if (elementDesc == null) { + for (int i = 0; i < length; i++) { + Object sourceElement = Array.get(source, i); + target.add(sourceElement); + } + } + // 元素需要转换 + else { + for (int i = 0; i < length; i++) { + Object sourceElement = Array.get(source, i); + // 调用注入的conversionService转换元素后,再添加 + Object targetElement = this.conversionService.convert(sourceElement, + sourceType.elementTypeDescriptor(sourceElement), elementDesc); + target.add(targetElement); + } + } + return target; + } +} +``` + +## 4. XmlBeanDefinitionReader + +[XmlBeanDefinitionReader](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java) +的主要功能是从`xml`中解析 bean 定义 + +```java +public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { + // 从一个来源处加载bean定义 + public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { + // 获取线程池来装载来源 + Set currentResources = this.resourcesCurrentlyBeingLoaded.get(); + + if (!currentResources.add(encodedResource)) { + // 如果添加失败,报错 + } + + try (InputStream inputStream = encodedResource.getResource().getInputStream()) { + InputSource inputSource = new InputSource(inputStream); + if (encodedResource.getEncoding() != null) { + inputSource.setEncoding(encodedResource.getEncoding()); + } + // 通过资源流来加载bean定义 + return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); + } + catch (IOException ex) { + // ... 代码省略 + } + finally { + // ... 代码省略 + } + } + + // 通过资源流来加载bean定义 + protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) + throws BeanDefinitionStoreException { + + try { + // 获取xml文件的Document对象 + Document doc = doLoadDocument(inputSource, resource); + int count = registerBeanDefinitions(doc, resource); + + // ... 代码省略 + + return count; + } + catch (BeanDefinitionStoreException ex) { + // ... 代码省略 + } + // ... 代码省略 + } + + public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { + // 实例化一个BeanDefinitionDocumentReader对象,默认使用DefaultBeanDefinitionDocumentReader + BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); + + // ... 代码省略 + + documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); + + // ... 代码省略 + } +} +``` + +本质上还是调用的 [DefaultBeanDefinitionDocumentReader.registerBeanDefinitions](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java#L94) + +```java +public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader { + // 注册bean定义 + @Override + public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { + this.readerContext = readerContext; + doRegisterBeanDefinitions(doc.getDocumentElement()); + } + + // 从doc根元素开始寻找并注册bean定义 + protected void doRegisterBeanDefinitions(Element root) { + BeanDefinitionParserDelegate parent = this.delegate; + // 实例化一个新的bean定义解析器 + this.delegate = createDelegate(getReaderContext(), root, parent); + + // ... 代码省略 + + // 解析bean定义 + parseBeanDefinitions(root, this.delegate); + + // ... 代码省略 + } + + // 解析bean定义 + protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { + // 如果bean定义的xmlns命名空间为空,或者为http://www.springframework.org/schema/beans + if (delegate.isDefaultNamespace(root)) { + NodeList nl = root.getChildNodes(); + // 遍历子节点 + for (int i = 0; i < nl.getLength(); i++) { + Node node = nl.item(i); + if (node instanceof Element) { + Element ele = (Element) node; + // 如果bean定义的xmlns命名空间为空,或者为http://www.springframework.org/schema/beans + // 做默认解析 + if (delegate.isDefaultNamespace(ele)) { + parseDefaultElement(ele, delegate); + } + // 不然做自定义解析 + else { + delegate.parseCustomElement(ele); + } + } + } + } + else { + // 解析自定义的元素 + delegate.parseCustomElement(root); + } + } +} +``` + +```java +public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader { + // 做默认解析 + private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { + // 如果是元素,导入其他资源 + if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { + importBeanDefinitionResource(ele); + } + // 如果是元素,注册别名 + else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { + processAliasRegistration(ele); + } + // 如果是元素,注册bean定义 + else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { + processBeanDefinition(ele, delegate); + } + // 如果是元素,解析子元素 + else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { + doRegisterBeanDefinitions(ele); + } + } + + // 如果是元素,注册bean定义 + protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { + // 解析bean的名字、别名、定义,包裹为BeanDefinitionHolder + BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); + if (bdHolder != null) { + + // ... 代码省略 + + try { + // 调用getReaderContext().getRegistry()注册bean定义 + BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); + } + catch (BeanDefinitionStoreException ex) { + // ... 代码省略 + } + // ... 代码省略 + } + } +} +``` + +再进一层,还是调用的 [BeanDefinitionParserDelegate](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java) + +```java +public class BeanDefinitionParserDelegate { + // 解析自定义的元素 + public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) { + // 获取xmlns命名空间 + String namespaceUri = getNamespaceURI(ele); + // 没有xmlns命名空间,返回null + if (namespaceUri == null) { + return null; + } + // 获取命名空间处理器,默认使用DefaultNamespaceHandlerResolver + // DefaultNamespaceHandlerResolver中,默认加载META-INF/spring.handlers指定的处理器 + // 核心的处理器是下面这些 + // http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler + // http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler + // http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler + // http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler + // http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler + // http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler + // http\://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler + // http\://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler + // http\://www.springframework.org/schema/mvc=org.springframework.web.servlet.config.MvcNamespaceHandler + // http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler + // http\://www.springframework.org/schema/jdbc=org.springframework.jdbc.config.JdbcNamespaceHandler + // http\://www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandler + NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); + + // ... 代码省略 + + // 调用处理器解析 + return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); + } +} +``` + +列举一下这些处理器,后面再解析: + +- [SimpleConstructorNamespaceHandler](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-beans/src/main/java/org/springframework/beans/factory/xml/SimpleConstructorNamespaceHandler.java) +- [SimplePropertyNamespaceHandler](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-beans/src/main/java/org/springframework/beans/factory/xml/SimplePropertyNamespaceHandler.java) +- [UtilNamespaceHandler](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-beans/src/main/java/org/springframework/beans/factory/xml/UtilNamespaceHandler.java) +- [ContextNamespaceHandler](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-context/src/main/java/org/springframework/context/config/ContextNamespaceHandler.java) +- [JeeNamespaceHandler](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-context/src/main/java/org/springframework/ejb/config/JeeNamespaceHandler.java) +- [LangNamespaceHandler](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-context/src/main/java/org/springframework/scripting/config/LangNamespaceHandler.java) +- [TaskNamespaceHandler](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-context/src/main/java/org/springframework/scheduling/config/TaskNamespaceHandler.java) +- [CacheNamespaceHandler](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-context/src/main/java/org/springframework/cache/config/CacheNamespaceHandler.java) +- [MvcNamespaceHandler](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-webmvc/src/main/java/org/springframework/web/servlet/config/MvcNamespaceHandler.java) +- [AopNamespaceHandler](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-aop/src/main/java/org/springframework/aop/config/AopNamespaceHandler.java) +- [JdbcNamespaceHandler](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-jdbc/src/main/java/org/springframework/jdbc/config/JdbcNamespaceHandler.java) +- [TxNamespaceHandler](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-tx/src/main/java/org/springframework/transaction/config/TxNamespaceHandler.java) + +```java +public class BeanDefinitionParserDelegate { + // 解析有bean定义的元素 + public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { + // 获取id属性 + String id = ele.getAttribute(ID_ATTRIBUTE); + // 获取name属性 + String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); + + List aliases = new ArrayList<>(); + if (StringUtils.hasLength(nameAttr)) { + // 用,;分隔为多个别名 + String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); + aliases.addAll(Arrays.asList(nameArr)); + } + + // 默认以id属性作为beanName + String beanName = id; + // 如果没有id属性,但有name属性,取第一个name作为beanName + if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { + beanName = aliases.remove(0); + } + + // ... 代码省略 + + // 真正解析bean定义 + AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); + if (beanDefinition != null) { + if (!StringUtils.hasText(beanName)) { + // 如果没有beanName,生成默认的beanName + try { + if (containingBean != null) { + beanName = BeanDefinitionReaderUtils.generateBeanName( + beanDefinition, this.readerContext.getRegistry(), true); + } + else { + beanName = this.readerContext.generateBeanName(beanDefinition); + + // ... 代码省略 + } + + } + catch (Exception ex) { + // ... 代码省略 + + return null; + } + } + String[] aliasesArray = StringUtils.toStringArray(aliases); + return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); + } + + return null; + } + + // 深层解析bean定义 + public AbstractBeanDefinition parseBeanDefinitionElement( + Element ele, String beanName, @Nullable BeanDefinition containingBean) { + + // class属性 + String className = null; + if (ele.hasAttribute(CLASS_ATTRIBUTE)) { + className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); + } + + // parent属性 + String parent = null; + if (ele.hasAttribute(PARENT_ATTRIBUTE)) { + parent = ele.getAttribute(PARENT_ATTRIBUTE); + } + + try { + // 以class、parent创建一个基础的定义对象 + AbstractBeanDefinition bd = createBeanDefinition(className, parent); + // 把其他属性应用到定义对象上,如singleton、abstract、lazy-init、autowire、depends-on等 + parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); + // 设置description属性 + bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); + + // 解析meta元素 + parseMetaElements(ele, bd); + // 解析lookup-method元素 + parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); + // 解析replaced-method元素 + parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); + + // 解析constructor-arg元素 + parseConstructorArgElements(ele, bd); + // 解析property元素 + parsePropertyElements(ele, bd); + // 解析qualifier元素 + parseQualifierElements(ele, bd); + + // ... 代码省略 + + return bd; + } + catch (ClassNotFoundException ex) { + // ... 代码省略 + } + // ... 代码省略 + + return null; + } +} +``` + +## 5. AnnotatedBeanDefinitionReader + +[AnnotatedBeanDefinitionReader](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java) +的主要功能是通过注解注册 bean + +```java +public class AnnotatedBeanDefinitionReader { + public void registerBean(Class beanClass) { + doRegisterBean(beanClass, null, null, null, null); + } + + public void registerBean(Class beanClass, @Nullable String name) { + doRegisterBean(beanClass, name, null, null, null); + } + + public void registerBean(Class beanClass, Class... qualifiers) { + doRegisterBean(beanClass, null, qualifiers, null, null); + } + + public void registerBean(Class beanClass, @Nullable String name, + Class... qualifiers) { + + doRegisterBean(beanClass, name, qualifiers, null, null); + } + + public void registerBean(Class beanClass, @Nullable Supplier supplier) { + doRegisterBean(beanClass, null, null, supplier, null); + } + + public void registerBean(Class beanClass, @Nullable String name, @Nullable Supplier supplier) { + doRegisterBean(beanClass, name, null, supplier, null); + } + + public void registerBean(Class beanClass, @Nullable String name, @Nullable Supplier supplier, + BeanDefinitionCustomizer... customizers) { + + doRegisterBean(beanClass, name, null, supplier, customizers); + } + + // 注册bean + private void doRegisterBean(Class beanClass, @Nullable String name, + @Nullable Class[] qualifiers, @Nullable Supplier supplier, + @Nullable BeanDefinitionCustomizer[] customizers) { + + // 创建一个基础的注解bean定义 + AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass); + + // ... 代码省略 + + // 确保beanName + String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry)); + + // 处理元信息通用的注解,如Lazy、Primary、DependsOn、Role、Description + AnnotationConfigUtils.processCommonDefinitionAnnotations(abd); + // qualifier注解的处理 + if (qualifiers != null) { + for (Class qualifier : qualifiers) { + if (Primary.class == qualifier) { + abd.setPrimary(true); + } + else if (Lazy.class == qualifier) { + abd.setLazyInit(true); + } + else { + abd.addQualifier(new AutowireCandidateQualifier(qualifier)); + } + } + } + + // 自定义处理器 + if (customizers != null) { + for (BeanDefinitionCustomizer customizer : customizers) { + customizer.customize(abd); + } + } + + // 封装为Holder + BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName); + + // ... 代码省略 + + // 注册到registry中 + BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry); + } +} +``` + +## 6. ClassPathBeanDefinitionScanner + +[ClassPathBeanDefinitionScanner](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java) +的主要功能是扫描指定包来获取 bean + +```java +public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider { + // 扫描包 + public int scan(String... basePackages) { + // ... 代码省略 + + // 扫描 + doScan(basePackages); + + // 注册注解解析处理器 + if (this.includeAnnotationConfig) { + // 主要是下面的bean + // org.springframework.context.annotation.internalConfigurationAnnotationProcessor => ConfigurationClassPostProcessor + // org.springframework.context.annotation.internalAutowiredAnnotationProcessor => AutowiredAnnotationBeanPostProcessor + // org.springframework.context.annotation.internalCommonAnnotationProcessor => CommonAnnotationBeanPostProcessor + // org.springframework.context.annotation.internalPersistenceAnnotationProcessor => PersistenceAnnotationBeanPostProcessor + // org.springframework.context.event.internalEventListenerProcessor => EventListenerMethodProcessor + // org.springframework.context.event.internalEventListenerFactory => DefaultEventListenerFactory + AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); + } + + // ... 代码省略 + } + + // 扫描 + protected Set doScan(String... basePackages) { + // 初始化容器 + Set beanDefinitions = new LinkedHashSet<>(); + + // 遍历包 + for (String basePackage : basePackages) { + // 获取包中可以处理的组件 + Set candidates = findCandidateComponents(basePackage); + + // 遍历组件 + for (BeanDefinition candidate : candidates) { + // 生成bean名字 + String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); + if (candidate instanceof AbstractBeanDefinition) { + // 注入一些默认值,并确定是否需要自动装配其他的bean + postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); + } + if (candidate instanceof AnnotatedBeanDefinition) { + // 处理元信息通用的注解,如Lazy、Primary、DependsOn、Role、Description + AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); + } + // 如果不存在,可添加 + if (checkCandidate(beanName, candidate)) { + BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); + + // ... 代码省略 + + beanDefinitions.add(definitionHolder); + // 注册到registry中 + registerBeanDefinition(definitionHolder, this.registry); + } + } + } + return beanDefinitions; + } + + // 获取包中可以处理的组件 + public Set findCandidateComponents(String basePackage) { + // ... 代码省略 + + return scanCandidateComponents(basePackage); + } + + // 获取包中可以处理的组件 + private Set scanCandidateComponents(String basePackage) { + Set candidates = new LinkedHashSet<>(); + try { + // 加上"classpath*:"前缀,"**/*.class"后缀 + String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + + basePackage + '/' + this.resourcePattern; + // 获取包下的全部类 + Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); + // 遍历资源 + for (Resource resource : resources) { + // 如果资源可读,继续进行 + if (resource.isReadable()) { + try { + // 获取元信息读取器 + MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); + // 初始化一个基础的bean定义 + ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); + sbd.setSource(resource); + + // 添加 + candidates.add(sbd); + } + catch (Throwable ex) { + // ... 代码省略 + } + } + else { + // ... 代码省略 + } + } + } + catch (IOException ex) { + // ... 代码省略 + } + return candidates; + } +} +``` + +列举一下这些处理器,后面再解析: + +- [ConfigurationClassPostProcessor](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java) +- [AutowiredAnnotationBeanPostProcessor](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java) +- [CommonAnnotationBeanPostProcessor](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java) +- [PersistenceAnnotationBeanPostProcessor](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java) +- [EventListenerMethodProcessor](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-context/src/main/java/org/springframework/context/event/EventListenerMethodProcessor.java) +- [DefaultEventListenerFactory](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-context/src/main/java/org/springframework/context/event/DefaultEventListenerFactory.java) + +## 后续 + +更多博客,查看 [https://github.com/senntyou/blogs](https://github.com/senntyou/blogs) + +作者:[深予之 (@senntyou)](https://github.com/senntyou) + +版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh))